Разбираемся вместе: строки в golang

Один из постов серии «Разбираемся вместе«: представляет из себя разбор определенной темы лично мной в целях улучшения понимания и возможно, получения фидбека от более опытных людей.
* Этот пост в совокупности с комментариями может помочь кому-то лучше понять разбираемую тему или сделать более подробный разбор на базе него.
* Этот пост не служит инструкцией, курсом или документацией, где гаранитируется 100% достоверная информация по озвученной теме — автор учится сам, так что ошибки возможны.

Строка (string) — это базовый (basic) тип данных, который представляет из себя неизменяемую последовательность байт, представленных в кодировке UTF-8.

Строки должны быть заключены в двойные кавычки.

  • Одинарные же кавычки представляют отдельный символ, который в Go представлен типом rune (псевдоним int32).

str := "A"

fmt.Println(str) // Выводит A

char := 'A'

fmt.Println(char) // Выводит 65 — символ это в первую очередь код Unicode

Про кодирование строк

Unicode — это стандарт, который определяет уникальные коды для каждого символа, независимо от используемой системы или языка.

  • Unicode — это также система кодирования символов, которая включает в себя огромный набор символов, каждый из которых имеет свой уникальный код. Эти коды называются Unicode code points.

UTF-8 (8-bit Unicode Transformation Format) — это один из способов (или схем) кодирования символов Unicode в последовательности байт.

  • Это позволяет эффективно хранить символы в памяти и передавать их по сети.

  • При кодировании в формате UTF-8 каждый символ может занимать от 1 до 4 байт, в зависимости от его позиции в стандарте Unicode.

  • Символы из ASCII (U+0000 до U+007F) кодируются одним байтом.

  • Символы из более широких диапазонов Unicode (например, кириллица, китайские иероглифы, эмодзи) могут занимать 2, 3 или 4 байта.

Индексация строк

Индексация строки по умолчанию работает с байтами, а не с символами.

  • При обращении по индексу,  возвращается тип byte.

  • Это отлично работает для символов ASCII, которые занимают 1 байт.

  • Для символов других алфавитов (например, кириллицы) через индексацию возвращается только первый байт, что может вызвать искажения при выводе символов.

Подробнее

Символы английского алфавита (символы ASCII) занимают 1 байт пространства.

  • то есть числовое представление символа ASCII, его код Unicode, умещается в 1 байт.

Поэтому каждый отдельный символ ASCII (вернее его код) можно получить через индекс.

s := “string”

fmt.Println(s[0]) // Выводит 83 — код символа в Unicode тип byte

fmt.Println(string(s[0])) // Выводит s — символ алфавита тип string

  • Делается каст типа byte в строку, чтобы привести символ в человекочитаемый вид.

Символы других алфавитов (или эмодзи) могут занимать более 1 байта. Поэтому через индекс нельзя получить полностью код Unicode. Вернется лишь значение первого байта.

  • Например, символы кириллицы занимают 2 байта пространства — числовое представление символа кириллицы, его код в Unicode не вмещается в 1 байт, поэтому происходит разделение на 2 байта.

s := “Привет”

fmt.Println(s[0]) // вернет Ð — код непонятен для кодировки utf-8, так как первый байт символа содержит лишь часть кода Unicode

  • символ кириллицы занимает 2 байта, через индекс забирается только первый байт, что искажает целевой символ

  • если взять срез fmt.Println(s[0:2]) // возвращается П — забираются первые 2 байта, которые представляют из себя код и код автоматически декодируется в понятный человеку символ.

Как работать с символами строк

Чтобы корректно работать с символами, нужно преобразовать строку в срез типа []rune.

  • Срез []rune представляет строку как последовательность Unicode-кодов символов.

s := “Привет”

runes := []rune(s)

fmt.Println(runes[0]) // 1055 (код символа П в Unicode)

fmt.Println(string(runes[0])) // каст кода П в строку


  • теперь каждый символ кириллицы правильно переведен в тип rune, что представляет код Unicode

Длина строки

Так как строки в Go представлены как последовательность байт, и их длина измеряется в байтах.

Если передать строку в функцию len(s), функция вернет размер строки в байтах.

Каждый отдельный английский символ (ASCII) занимает 1 байт памяти и размер строки в байтах включающий только ASCII символы будет равен длине строки.

  • Может возникнуть неточность, если строка состоит из символов другого языка или включает эмодзи, в этом случае размер строки будет превышать фактическую длину, так как символы других алфавитов занимают больше 1 байта пространства.

Для того чтобы получить количество символов (вместо байтов), можно использовать пакет unicode/utf8.

import “unicode/utf8”

s := “Это строка”

fmt.Println(len(s)) // 19

fmt.Println(utf8.RuneCountInString(s)) // 10

Еще один способ получить длину строки (количество символов) это сделать каст строки в срез с элементами типа rune.

s := “Это строка”

fmt.Println(len([]rune(s))) // 10

Эти два способа гарантированно вернут правильное количество символов в строке, даже если в ней содержатся символы разных алфавитов.

Литералы строк

Литерал — это фиксированное значение, напрямую указанное в исходном коде программы.

Литерал представляет собой синтаксическую конструкцию, которая интерпретируется компилятором или интерпретатором как экземпляр определённого типа данных.

  • С помощью литерала компилятор или интерпретатор понимает, с каким типом данных работает, анализируя его синтаксическое представление (например, кавычки для строк, числовой формат для целых и вещественных чисел, фигурные скобки для коллекций и т.д.).

Литерал строки — это текст в исходном коде программы.

  • Кавычки, которые окружают литерал строки, — это синтаксическая часть языка Go, они нужны для обозначения строки.

  • С помощью двойных кавычек, компилятор golang поймет, что он работает со строкой.

  • Если в значении строки требуются двойные кавычки (например, для цитаты), их нужно явно указать и экранировать в самом литерале строки.

Значение строки — это результат выполнения программы, отображаемый в терминале или другом выходном потоке, без включения кавычек.

Управляющие символы

Для форматирования строк и работы с особыми символами в Go используются управляющие символы.

Управляющие символы начинаются с обратного слеша (\), за которым следует другой символ, определяющий их действие.

  • Они используются для добавления специальных символов, которые невозможно напрямую ввести в строку, либо для экранирования тех символов, которые иначе воспринимались бы как часть синтаксиса.

Примеры управляющих символов в Go:

  • \n — перевод строки

  • \t — символ табуляции

  • \f — подача страницы

  • \\ — обратный слеш

  • \" — экранирование двойной кавычки

  • \' — экранирование одинарной кавычки

Многострочная печать

Размещение строк в нескольких рядах делает текст более понятным и упорядоченным, позволяет форматировать его как письмо или сохранить разрывы строк в стихотворении или тексте песни.

Для создания строк, отображаемых на нескольких рядах, их нужно заключить в обратные апострофы.

  • В строках, заключенных в обратные апострофы, не требуется экранировать символы. Например, символы \ или кавычки (") можно использовать без обратного слэша.

Такие строки называют "сырыми" (raw string literals), так как они сохраняют текст в его исходном виде без обработки управляющих символов.

text := `This is a

multi-line string.

It preserves line breaks and spaces.`

Конкатенация

Оператор + используется для соединения двух строк.

s1 := "Hello, "

s2 := "world!"

s3 := s1 + s2

fmt.Println(s3) // "Hello, world!"


Эта операция создаёт новую строку, которая является результатом соединения исходных строк.

  • Важно понимать, что строки в Go неизменяемы, поэтому при использовании операции + возвращается новое значение, а не изменяется одна из исходных строк.

Пакет strings

Встроенный пакет, который предоставляет функции для работы со строками в кодировке utf-8.

  • с полным набором функций можно ознакомиться в документации пакета, тут разберем некоторые

strings.Contains()

Эта функция проверяет, содержится ли подстрока в строке.

  • Возвращает true, если строка найдена, и false — если не найдена.

import "strings"

s := "hello world"

fmt.Println(strings.Contains(s, "world")) // true

fmt.Println(strings.Contains(s, "go"))  // false

strings.ToUpper() и strings.ToLower()

Эти функции позволяют преобразовать строку в верхний или нижний регистр.

import "strings"

s := "Hello"

fmt.Println(strings.ToUpper(s)) // "HELLO"

fmt.Println(strings.ToLower(s)) // "hello"

strings.Trim()

Функция strings.Trim() позволяет удалить указанный вторым параметром символы с начала и конца строки.

import "strings"

s := "  Hello, world!  "

fmt.Println(strings.Trim(s, " ")) // Выведет "Hello, world!"

strings.TrimSpace()

Можно также использовать strings.TrimSpace(), чтобы удалить только пробелы.

strings.Split()

Функция strings.Split() разделяет строку на подстроки по заданному разделителю и возвращает срез строк.

import "strings"

s := "a b c d"

result := strings.Split(s, " ")

fmt.Println(result) // [a b c d]

strings.Join()

Функция strings.Join() объединяет срез строк в одну строку с заданным разделителем.

import "strings"

slice := []string{"a", "b", "c", "d"}

result := strings.Join(slice, "")

fmt.Println(result) // "abcd"

strings.Replace()

Эта функция позволяет заменить все или ограниченное количество вхождений подстроки в строке на другую строку.

import "strings"

s := "hello world"

result := strings.Replace(s, "world", "Go", -1)

fmt.Println(result) // "hello Go"

  • -1 все вхождения

range

range — это ключевое слово для перебора коллекций в цикле.

При каждом проходе цикла range возвращает два значения:

  • индекс начального байта символа строки

  • индекс элемента (массив или срез)

  • ключ при работе с картами

  • и затем соответствующее значение из коллекции.

Со строками ключевое слово range используется для итерации и извлечения каждого символа (rune) из строки.

  • Это позволяет корректно обрабатывать многобайтовые символы в строках.


s := "string"

for i, r := range s {

fmt.Printf("%d: %c\n", i, r)

}

  • В этом примере i — это начальный индекс байта в строке, а r — это сам символ (rune).

fmt.Sprintf()

Аналог fmt.Printf(), но результат сохраняется в строку, а не выводится в консоль.

str := fmt.Sprintf("Hello, %s!", "world")

fmt.Println(str)

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества