Литералы, операторы и экранированные последовательности
Строковые литералы определяются с помощью кавычек (") или обратных апострофов (`). Кавычки используются для определения интерпретируемых строковых литералов – такие строки поддерживают экранированные последовательности , перечисленные в табл. 3.1, но они не могут занимать несколько строк в программе. Обратные апострофы используются для определения обычных строковых литералов, такие строки могут занимать несколько строк в программе, но они не поддерживают экранированных последовательностей и могут содержать любые символы, кроме обратных апострофов. Интерпретируемые строковые литералы используются чаще, но для записи многострочных сообщений, разметки HTML и регулярных выражений удобнее использовать строковые литералы в обратных апострофах. Ниже приводятся несколько примеров литералов.
text1 := "\"what’s that?\", he said" // Интерпретируемый строковый литерал
text2 := `"what’s that?", he said` // Простой строковый литерал
radicals := " \u221A \U0000221a" // radicals == " "
Таблица 3.1 Экранированные последовательности
Экранированная последовательность |
Описание |
\\ |
Обратный слеш (\) |
\000 |
Символ Юникода, соответствующий 8-битному кодовому пункту, заданному тремя восьмеричными цифрами |
\' |
Апостроф ('), может использоваться только внутри символьных литералов |
\" |
Кавычки ("), может использоваться только внутри интерпретируемых строковых литералов |
\a |
ASCII-символ «сигнал» (BEL) |
\b |
ASCII-символ «забой» (BS) |
\f |
ASCII-символ «перевод формата» (FF) |
\n |
ASCII-символ «перевод строки» (LF) |
\r |
ASCII-символ «возврат каретки» (CR) |
\t |
ASCII-символ «табуляция» (TAB) |
\uhhhh |
Символ Юникода, соответствующий 16-битному кодовому пункту, заданному четырьмя шестнадцатеричными цифрами |
\Uhhhhhhhh |
Символ Юникода, соответствующий 32-битному кодовому пункту, заданному восемью шестнадцатеричными цифрами |
\v |
ASCII-символ «вертикальная табуляция» (VT) |
\xhh |
Символ Юникода, соответствующий 8-битному кодовому пункту, заданному двумя шестнадцатеричными цифрами |
В этом примере были созданы три переменные типа string, при этом переменные text1 и text2 содержат один и тот же текст. Поскольку для файлов с расширением .go используется кодировка UTF-8, в них можно включать любые символы Юникода. Однако сохраняется возможность использовать экранированные последовательности Юникода, как это сделано для второго и третьего символов . Здесь невозможно использовать 8-битное восьмеричное или шестнадцатеричное представление кодового пункта, так как они ограничены диапазоном от U+0000 до U+00FF, слишком узкого для представления кодового пункта U+221A, соответствующего символу .
Если потребуется определить длинный и интерпретируемый строковый литерал, разместив его на нескольких строках в тексте программы, можно разбить его на несколько литералов и объединить их оператором конкатенации (+). Кроме того, несмотря на то что строки в языке Go являются неизменяемыми, они поддерживают оператор добавления += . Он замещает имеющуюся строку результатом конкатенации двух строк, если емкости исходной строки недостаточно для размещения добавляемой строки. Эти операторы перечислены в табл. 3.2. Строки могут сравниваться с помощью операторов сравнения. Ниже демонстрируется использование этих операторов:
book := "The Spirit Level" + // Конкатенация строк
" by Richard Wilkinson"
book += " and Kate Pickett" // Добавление в конец строки
fmt.Println("Josey" < "José", "Josey" == "José") // Сравнение строк
В результате выполнения этого фрагмента переменная book будет содержать текст «The Spirit Level by Richard Wilkinson and Kate Pickett», а в поток os.Stdout будет выведена строка «true false».
Сравнение строк
Как уже отмечалось, строки в языке Go поддерживают обычные операторы сравнения (<, <=, ==, !=, >, >=). Сравнение строк этими операторами выполняется побайтно.
Строки могут сравниваться непосредственно, например на равенство, и косвенно, например когда оператор < используется для сравнения строк с целью сортировки содержимого среза []string. К сожалению, при выполнении сравнения могут возникать три проблемы. Эти проблемы проявляются во всех языках программирования, поддерживающих строки Юникода, и не являются характерными только для языка Go.
Оператор [] извлечения среза без всяких ограничений может применяться только к строкам из 7-битных символов ASCII, во всех остальных случаях необходимо проявлять осторожность. Строки могут сравниваться с помощью стандартных операторов сравнения: <, <=, ==, !=, >=, >.
Таблица 3.2 Операции со строками
Операция |
Описание/результат |
s += t |
Добавляет строку t в конец строки s |
s + t |
Конкатенация строк s и t |
s[n] |
Байт (значение типа uint8) в позиции n, в строке s |
s[n:m] |
Подстрока, извлеченная из строки s, начиная с позиции n по m-1 |
s[n:] |
Подстрока, извлеченная из строки s, начиная с позиции n по len(s)-1 |
s[:m] |
Подстрока, извлеченная из строки s, начиная с позиции 0 по m-1 |
len(s) |
Количество байт в строке s |
len([] rune(s)) |
Количество символов в строке s; то же значение можно получить намного быстрее с помощью функции utf8.RuneCountInString() |
[]rune(s) |
Преобразует строку в срез кодовых пунктов Юникода |
string (символы) |
Преобразует значение типа []rune или []int32 в строку; предполагается, что значения типа rune или int32 представляют кодовые пункты Юникода |
[]byte(s) |
Преобразует строку s типа string в срез байт без копирования; нет никакой гарантии, что байты будут допустимыми значениями в кодировке UTF-8 |
string (байты) |
Преобразует значение типа []byte или []uint8 в строку типа string без копирования; не требуется, чтобы байты были допустимыми значениями в кодировке UTF-8 |
string(i) |
Преобразует значение i любого целочисленного типа в значение типа string; предполагается, что i представляет кодовый пункт Юникода; например, если i == 65, программе будет возвращено значение «A» |
strconv.Itoa(i) |
Вернет строковое представление числа i типа int и значение ошибки; например если i == 65, программе будут возвращены два значения («65», nil) |
fmt.Sprint(x) |
Вернет строковое представление значения x любого типа; например если x является целым числом, равным 65, программе будет возвращена строка «65» |
Символы в языке Go могут быть представлены двумя разными (но взаимозаменяемыми) способами. Единственный символ может быть представлен значением типа rune (или int32). С этого момента термины «символ», «кодовый пункт», «символ Юникода» и «кодовый пункт Юникода» будут использоваться взаимозаменяемо для ссылки на значение типа rune (или int32), хранящее единственный символ. Строки в языке Go представлены последовательностями из нуля или более символов – каждый символ внутри строки представлен одним или более байт в кодировке UTF-8.
С помощью операции преобразования типа (string(символ)) единственный символ можно преобразовать в односимвольную строку.
Например:
đs := ""
for _, char := range []rune{‘đ’, 0x111, 0421, 273, '\u0111'} {
fmt.Printf("[0x%X ‘%c’] ", char, char)
đs += string(char)
}
Этот фрагмент выведет строку, в которой текст [0x111 ‘đ’] повторяется пять раз, а после его выполнения переменная đs будет содержать строку, содержащую текст đđđđđ.
Преобразовать строку в срез со значениями типа rune (то есть кодовых пунктов) можно с помощью операции преобразования chars := []rune(s), где s – значение типа string. Значение chars в этом случае будет иметь тип []int32, поскольку тип rune является синонимом типа int32. Такая возможность может пригодиться, например, когда потребуется выполнить посимвольный анализ строки и при этом выбирать символы, стоящие перед и после текущего. Обратное преобразование выполняется так же просто: s := string(chars), где значение chars имеет тип []rune, или []int32, а значение s будет иметь тип string.
Несмотря на удобство, оператор += обеспечивает не самый эффективный способ наращивания строк в циклах. Более удачный способ заключается в заполнении среза со строками ([]string) с последующим объединением его элементов вызовом функции strings.Join().
Цикл for ...range можно использовать для итераций по символам строки. В этом случае в каждой итерации программе становятся доступны индекс текущей позиции в строке и кодовый пункт в этой позиции. Ниже приводятся пример использования этой версии цикла и посимвольный вывод строки написанной на греческом языке и означающей «Язык программирования». Результат работы приложения представлен на рисунке 3.1.
phrase := "Γλώσσα προγραμματισμού"
fmt.Printf("string: \"%s\"\n", phrase)
fmt.Println("index\trune\t\tchar\tbytes")
for index, char := range phrase {
fmt.Printf("%-2d\t%U\t\t‘%c’\t% X\n", index, char, char, []byte(string(char)))
}
В начале фрагмента создается строковый литерал phrase и в следующей строке выводится на экран. Затем выполняются итерации по символам в строке – в языке Go цикл for ...range автоматически декодирует байты UTF-8 в кодовые пункты Юникода (значения типа rune), поэтому нет необходимости беспокоиться о внутреннем их представлении. Для каждого символа выводится номер его позиции, значение кодового пункта (в форме записи, принятой в стандарте Юникода), сам символ и соответствующие ему байты в кодировке UTF-8.
Рисунок 3.1 Результат работы приложения
Чтобы получить список байтов, кодовые пункты (значения char типа rune) преобразуются в строку (содержащую единственный символ, который состоит из одного или более байтов в кодировке UTF-8). Затем эта односимвольная строка преобразуется в значение типа []byte, то есть в срез с байтами, благодаря чему появляется возможность доступа к фактическим байтам. Преобразование []byte(string) выполняется очень быстро (O(1)), так как []byte просто ссылается на внутреннее представление строки string, без необходимости копировать какие-либо данные. То же справедливо и для обратного преобразования string([]byte) – здесь байты внутреннего представления строки также никуда не копируются, поэтому данное преобразование тоже имеет сложность O(1). Преобразования между строками и последовательностями байтов перечислены в табл. 3.2 (выше).
