После изучения данной статьи вы сможете:
- Получить доступ к отдельным буквам, а также манипулировать ими;
- Зашифровать и расшифровать сообщение;
- Написать программу для людей, что говорят на разных языках.
С самого начала изучения программирования при наборе простого кода «Hello, playground» вы уже задействовали текст. Буквы, цифры и различные знаки называют символами (characters). Символы, помещенные между кавычками, называют строковым литералом (literal string).
![](https://golangify.com/wp-content/uploads/2020/04/go-read.png)
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
Содержание статьи
- Объявление строковых переменных Go
- Необработанные строковые литералы Golang
- Символы, коды, rune и byte
- Можно ли изменять строки в Golang?
- Манипуляция символами с помощью шифра Цезаря
- ROT13 — современный вариант шифра Цезаря
- Декодирование строк в руны
Наверняка вам известно, что компьютеры оперируют через нули и единицы. Будучи компьютером, как бы вы переписали алфавит, а заодно и человеческий язык? С помощью цифр, верно. У символов алфавита есть цифровые значения, следовательно, ими можно манипулировать как числами.
Однако, все не так просто. У многих языков есть уникальные знаки, плюс ко всему к тысячам символов добавляются многочисленные эмодзи. В Go есть свои способы представления текста в наиболее удобном и простом виде.
Объявление строковых переменных Go
Литеральные значения, помещенные в кавычки, относятся к типу string
. Следующие три строки кода эквивалентны:
1 2 3 |
peace := "peace" var peace = "peace" var peace string = "peace" |
При объявлении переменной без указания значения она будет инициализирована с нулевым значением. Нулевым значением типа string являются пустые кавычки (""
):
1 |
var blank string |
Необработанные строковые литералы Golang
В строковых литералах могут находиться специальные экранированные символы наподобие \n
. Во избежание замены \n
новой строкой можно поместить текст в обратные кавычки `
вместо обычных "
, как показано в примере ниже. Обратные кавычки выводят строку в необработанном виде.
1 2 |
fmt.Println("peace be upon you\nupon you be peace") fmt.Println(`strings can span multiple lines with the \n escape sequence`) |
Вывод предыдущего листинга:
1 2 3 |
peace be upon you upon you be peace strings can span multiple lines with the \n escape sequence |
В отличие от обычных литеральных строк необработанные строки могут охватывать несколько строк исходного кода, как показано в примере ниже:
1 2 3 |
fmt.Println(` peace be upon you upon you be peace`) |
Результат листинга 2, включая отступы:
1 2 |
peace be upon you upon you be peace |
Как строковые литералы, так и необработанные строковые литералы выводят данные типа string
, как показано в примере ниже.
1 2 |
fmt.Printf("%v is a %[1]T\n", "literal string") // Выводит: literal string is a string fmt.Printf("%v is a %[1]T\n", `raw string literal`) // Выводит: raw string literal is a string |
Вопрос для проверки:
Для пути Windows C:\go
лучше использовать строковой литерал или необработанный строковой литерал? Почему?
Символы, коды, rune и byte
Unicode Consortium присваивает числовые значения, или коды, более миллиону уникальных символов. К примеру, 65 является кодом заглавной буквы А, а 1285515 — смайлика ?.
Для представления Юникода в Go используется тип rune
, который иначе называется int32
.
byte
является другим названием типа uint8
. Он используется для бинарных данных, хотя byte
может использоваться для символа английского алфавита, определенного ASCII, более старого 128-символьного набора Юникод.
Разные названия одного типа: В Go у одного и того же типа могут быть разные названия. К примеру,
rune
иint32
являются взаимозаменяемыми. Хотяbyte
иrune
были в Go с самого начала, с появлением версии Go 1.9 стало возможным объявлять свои собственные названия типов. Синтаксис выглядит следующим образом:
12 type byte = uint8type rune = int32
Оба byte
и rune
ведут себя также, как и типы целых чисел, другими названиями которых они являются. Это показано в следующем листинге:
1 2 3 4 5 6 |
var pi rune = 960 var alpha rune = 940 var omega rune = 969 var bang byte = 33 fmt.Printf("%v %v %v %v\n", pi, alpha, omega, bang) // Выводит: 960 940 969 33 |
Для отображения символов вместо их числовых значений можно использовать специальный символ %c
вместе с Printf
:
1 |
fmt.Printf("%c%c%c%c\n", pi, alpha, omega, bang) // Выводит: πάω! |
Вместе с %с
будет работать любой целочисленный тип, однако название rune
является своеобразной подсказкой, что число 960 на самом деле является каким-то символом.
Запоминать все Юникоды нет нужды, в Go есть символьный литерал. Просто поместите символ в одинарные кавычки: 'A'
. Если тип не уточняется, Go присвоит тип rune
. Следующие три строки кода эквиваленты:
1 2 3 |
grade := 'A' var grade = 'A' var grade rune = 'A' |
Переменная grade
по-прежнему содержит числовое значение, в данном случае 65
, то есть код для заглавной буквы 'A'
. Символьные литералы также можно использовать для типа с названием byte
:
1 |
var star byte = '*' |
Вопросы для проверки:
- Сколько символов кодирует ASCII?
- Другим названием какого типа является
byte
? Аrune
? - Какой код используется для звездочки
*
, смайлика ? и буквые
со знаком ударенияé
?
Можно ли изменять строки в Golang?
Строки в Golang не предназначены для манипуляции. Переменной можно присвоить другую строку, однако строки сами по себе редактировать нельзя:
1 2 |
peace := "shalom" peace = "salām" |
Программа может получить доступ к отдельным символам, но не может редактировать саму строку. В следующем листинге используются квадратные скобки []
для уточнения индекса внутри строки, что обращается к одному байту (символ ASCII). Индексы начинаются с нуля.
1 2 3 |
message := "shalom" c := message[5] fmt.Printf("%c\n", c) // Выводит: m |
Строки Go неизменны, их также нельзя менять в таких языках, как Python, Java и JavaScript. В отличие от строк в Ruby и массивов символов в С, строку в Go отредактировать нельзя:
1 |
message[5] = 'd' // Нельзя присвоить message[5] |
Задание для проверки:
Напишите программу для вывода каждого байта (символ ASCII) слова «shalom», по одному символу на строку.
Манипуляция символами с помощью шифра Цезаря
Эффективным методом отправки секретных сообщений во втором веке был сдвиг букв. Таким образом, a
становится d
, b
становится e
и так далее. В результате получается странный текст, будто написанный на иностранном языке: L fdph, L vdz, L frqtxhuhg.
Как оказалось, манипуляция символами как цифровыми значениями дается компьютерам очень легко. Это показано в коде ниже:
1 2 3 |
c := 'a' c = c + 3 fmt.Printf("%c", c) // Выводит: d |
У данного кода есть проблема. Он не подойдет для сообщений слов с буквами ‘x’, ‘y’ и ‘z’, то есть тремя последними буквами английского алфавита. Избавиться от данного неудобства можно с помощью переполнения значений, то есть возврата к началу алфавита. Таким образом, ‘x’ станет ‘а’, ‘y’ станет ‘b’, а ‘z’ станет ‘c’. С 26 знаками английского алфавита это достаточно просто:
1 2 3 |
if c > 'z' { c = c - 26 } |
Чтобы декодировать данный шифр Цезаря, нужно убрать 3, а не добавлять. Однако в таком случае вам придется учитывать, что c < ‘a’, добавляя 26.
Вопрос для проверки:
Каким будет результат выражения c = c — ‘a’ + ‘A’, если с
является 'g'
в нижнем регистре?
ROT13 — современный вариант шифра Цезаря
ROT13 (rotate 13, сдвинуть на 13 позиций) является современным вариантом шифра Цезаря. С одним отличием: сдвиг происходит не на 3, а на 13 символов. Вместе с ROT13 шифрование и дешифрование становятся одинаково удобными операциями.
Предположим, что при сканировании воздушной оболочки на наличие иноземных объектов Институт SETI получил сообщение, содержание которого выглядит следующим образом:
1 |
message := "uv vagreangvbany fcnpr fgngvba" |
Есть подозрение, что message
на самом деле является английским текстом, зашифрованным с ROT13. Перед взломом кода вам нужно кое-что знать. Длина message
составляет 30 символов, что можно определить через встроенную функцию len
:
1 |
fmt.Println(len(message)) // Выводит: 30 |
В Go есть удобные встроенные функции, которые можно использовать без оператора импорта пакетов. Функция len
определяет длину различных типов. В данном случае len
возвращает длину string
в байтах.
Следующий листинг дешифрует сообщение из космоса. Запустите код на Go Playground и выясните, что за сообщение передают инопланетяне.
1 2 3 4 5 6 7 8 9 10 11 12 |
message := "uv vagreangvbany fcnpr fgngvba" for i := 0; i < len(message); i++ { // Итерирует каждый символ ASCII c := message[i] if c >= 'a' && c <= 'z' { // Оставляет оригинальную пунктуацию и пробелы c = c + 13 if c > 'z' { c = c - 26 } } fmt.Printf("%c", c) } |
Обратите внимание, что реализация ROT13 в данном коде предназначена только для символов ASCII (байтов). Сообщение, написанное на русском или испанском, пришлось бы расшифровывать иначе. Поговорим об этом далее.
Вопросы для проверки:
- Для чего нужна встроенная функция
len
? - Запустите Листинг 7 на Go Playground? Что говорится в сообщении?
Декодирование строк в руны
Строки в Go закодированы в UTF-8, одним из нескольких стандарт кодирования юникод символов. UTF-8 является эффективным кодированием переменной длины. Один код может использовать 8, 16 или 32 бита памяти. Используя кодирование переменной длины, UTF-8 делает перевод из ASCII довольно несложным, ведь символ ASCII идентичным своим зашифрованным вариантам из UTF-8.
На заметку: В Интернете UTF-8 является доминирующим стандартом кодировки. Он был введен в 1992 году Кеном Томпсоном, одним из дизайнеров Go.
Программа с ROT13 из Листинга 7 получает доступ к отдельным байтам (8-bit) строки message
без подсчета символов длиной в большее количество байт (16-bit или 32-bit). Это неплохо подходит для английских символов (ASCII), но в случае русского или испанского — получается непонятная мешанина. Ты способен на лучшее, амиго.
Первым шагом на пути к поддержке других языков является декодирование символов в тип rune
, что делается перед манипуляцией ими. К счастью, у Go есть функции и другие особенности языка для декодирования закодированных UTF-8 строк.
Пакет utf8
предоставляет функции для определения длины строки в рунах, а не байтах, а также декодирования первого символа строки. Функция DecodeRuneInString
возвращает первый символ и количество байт, что символ тратит, как показано в Листинге 8.
На заметку: В отличие от многих других языков программирования, функции в Go могут вернуть много значений. Множественные возвращаемые значения будут подробно рассмотрены в одном из следующих уроков.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package main import ( "fmt" "unicode/utf8" ) func main() { question := "¿Cómo estás?" fmt.Println(len(question), "bytes") // Выводит: 15 bytes fmt.Println(utf8.RuneCountInString(question), "runes") // Выводит: 12 runes c, size := utf8.DecodeRuneInString(question) fmt.Printf("First rune: %c %v bytes", c, size) // Выводит: First rune: ¿ 2 bytes } |
В языке Go есть ключевое слово range
для итерирования разнообразных наборов. Оно также может декодировать закодированные строки UTF-8, как показано в следующем листинге.
1 2 3 4 5 |
question := "¿Cómo estás?" for i, c := range question { fmt.Printf("%v %c\n", i, c) } |
При каждой итерации переменные i
и c
присваиваются текущий индекс в строке и коду (rune
) на данной позиции.
Если вам не нужен индекс, пустой идентификатор (нижнее подчеркивание) позволяет проигнорировать его:
1 2 3 |
for _, c := range question { fmt.Printf("%c ", c) // Выводит: ¿ C ó m o e s t á s ? } |
Вопросы для проверки:
- Сколько рун в английском алфавите
"abcdefghijklmnopqrstuvwxyz"
? Сколько байтов? - Сколько байтов в руне
'¿'
?
Заключение
- Экранированные символы вроде
\n
игнорируются в необработанных строковых литералах (`
); - Строки не изменяются. К отдельным символам можно получить доступ, но изменить их нельзя;
- Строки используют кодировку под названием UTF-8, где каждому символу требуется от 1 до 4 байтов;
byte
является другим названием типаuint8
, arune
является другим названием типаint32
;- Ключевое слово
range
может декодировать закодированную строку UTF-8 в руны.
Итоговое задание для проверки #1:
Расшифруйте цитату Юлия Цезаря: L fdph, L vdz, L frqtxhuhg.
Ваша программа должна будет сдвинуть буквы верхнего и нижнего регистра на -3. Помните, что ‘a’ становится ‘x’, ‘b’ становится ‘y’, а ‘c’ становится ‘z’. То же самое происходит с буквами верхнего регистра.
Итоговое задание для проверки #2:
Зашифруйте сообщение на испанском: “Hola Estación Espacial Internacional” через ROT13. Модифицируйте Листинг 7 с использованием ключевого слова range
. Теперь, когда вы используете ROT13 c испанским текстом, ударение над буквами сохраняется.
![](https://golangify.com/wp-content/uploads/2021/01/v.jpg)
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
Здравствуйте.я сделал вот такой код и он тоже работает и он чуть покороче так как нету смысла проверять заглавные они в любом случае не могут быть вне диапазона уходящего за границу заглавной «A»
Как вы считаете так можно?
mess := «L fdph, L vdz, L frqtxhuhg.»
for i := 0; i < len(mess); i++ {
c := mess[i]
if c >= ‘a’ && c <= ‘z’ || c >= ‘A’ && c <= ‘Z’ {
c -= 3
}
fmt.Printf(«%c», c)
}