Символьные типы в Golang

GolangBeginner
Практиковаться сейчас

Введение

В предыдущем разделе мы рассмотрели наиболее часто используемые числовые типы. В этой части мы перейдем к изучению символьных типов данных в языке Go.

Основные темы:

  • Кодировка ASCII
  • Кодировка UTF-8
  • Набор символов Unicode
  • Тип byte
  • Тип rune

Кодировка ASCII

На заре компьютерной эры использовался формат кодирования ASCII (American Standard Code for Information Interchange). В нем для представления символа использовалось 7 бит, что позволяло закодировать 128 (2^7) различных знаков. Символы с кодами от 0 до 31, а также 127, являются управляющими и не отображаются на экране. Символы с кодами от 32 до 126 включают привычные нам латинские буквы (заглавные и строчные), цифры и знаки препинания. Подробности можно найти в таблице ASCII.

С развитием технологий возникла необходимость поддерживать разные языки мира. Кодировки ASCII для этих целей было недостаточно. В результате в разных странах появились свои стандарты, такие как GB2312 для упрощенного китайского, EUC-KR для корейского и KOI8-R для русского языка.

Однако наличие множества разрозненных кодировок создавало путаницу. Возникла потребность в едином стандарте, который мог бы объединить все языки мира. Так появился Unicode.

Набор символов Unicode

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

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

Кодировка UTF-8

С развитием интернета широкое распространение получила кодировка UTF-8 — один из способов реализации Unicode. Это кодировка с переменной длиной символа: разные знаки в UTF-8 могут занимать разное количество байтов.

Например, латинские буквы, входящие в диапазон ASCII, занимают всего 1 байт. Символ 'y' (код Unicode 121) весит 1 байт.

Большинство часто используемых китайских иероглифов занимают 3 байта. Например, иероглиф '实' (код Unicode 23454) требует 3 байта.

Существуют и более редкие символы, которые занимают 4 байта. Это связано с тем, что в китайском языке более 100 000 иероглифов, а трехбайтовая схема позволяет закодировать чуть более 60 000 значений.

Еще одно преимущество UTF-8 — обратная совместимость с ASCII. По сути, ASCII является подмножеством UTF-8. Первые 128 символов UTF-8 полностью совпадают с ASCII. Это означает, что старое программное обеспечение может работать с текстами UTF-8 без серьезных доработок. Благодаря этим достоинствам UTF-8 стала стандартом де-факто.

Создатели языка Go, Роб Пайк и Кен Томпсон, также являются авторами UTF-8, поэтому в Go реализована нативная поддержка этой кодировки. Исходные файлы Go должны быть сохранены именно в UTF-8. При работе с текстом UTF-8 является приоритетным выбором, а стандартная библиотека Go предоставляет множество функций для кодирования и декодирования таких данных.

byte и rune

Тип byte является псевдонимом для uint8 и занимает один байт (8 бит). Он идеально подходит для представления всех символов таблицы ASCII. Однако, поскольку диапазон значений byte ограничен (256 значений или 2^8), для работы со сложными символами, такими как иероглифы или эмодзи, нам необходим тип rune.

Создайте новый файл с именем byte.go и введите следующий код:

cd ~/project
touch byte.go
package main

import "fmt"

func main() {
    var a byte = 76
    fmt.Printf("Value of a: %c\n", a)

    var b uint8 = 76
    fmt.Printf("Value of b: %c\n", b)

    var c byte = 'L'
    fmt.Printf("Value of c: %c\n", c)
}

После запуска программы вы увидите следующий результат:

go run byte.go
Value of a: L
Value of b: L
Value of c: L

Спецификатор %c используется для вывода символьного представления значения. Как видите, типы byte и uint8 ведут себя идентично при одинаковых значениях. Если заглянуть в таблицу ASCII, можно увидеть, что коду 76 соответствует буква 'L'. Если использовать спецификатор %d для вывода целого числа, мы получим то же самое числовое значение.

Таким образом, byte в Go — это то же самое, что и целое число uint8. Аналогичная логика применима и к rune, но этот тип охватывает гораздо больший диапазон значений.

Тип rune является псевдонимом для int32 и занимает четыре байта (32 бита). Он используется для представления сложных символов Unicode, включая эмодзи.

Обновите файл byte.go, используя следующий код:

package main

import "fmt"

func main() {
    var a rune = '😊' // Смайлик
    fmt.Printf("Value of a: %c\n", a)

    var b int32 = 9829 // Десятичное представление символа Unicode (Сердце)
    fmt.Printf("Value of b: %c\n", b)
    var c rune = 0x1F496 // Шестнадцатеричное представление символа Unicode (Сияющее сердце)
    fmt.Printf("Value of c: %c\n", c)

    var d rune = '\u0041' // Символ Unicode через кодовую точку (Заглавная 'A')
    fmt.Printf("Value of d: %c\n", d)
    var e rune = '\U0001F609' // Символ Unicode через кодовую точку (Подмигивающий смайлик)
    fmt.Printf("Value of e: %c\n", e)
}

Запустите программу, чтобы увидеть результат:

go run byte.go

Примечание: Запускайте программу в терминале Desktop или WebIDE. Избегайте запуска во вкладке Terminal, расположенной в верхней части виртуальной машины LabEx.

Value of a: 😊
Value of b: ♥
Value of c: 💖
Value of d: A
Value of e: 😉
  • Переменная a содержит эмодзи '😊'.
  • Переменная b инициализирована десятичным кодом Unicode (9829), что соответствует символу сердца '♥'.
  • Переменная c инициализирована шестнадцатеричным кодом (0x1F496), соответствующим эмодзи '💖'.
  • Переменная d представляет букву 'A' через формат Unicode \u0041.
  • Переменная e представляет подмигивающий смайлик через формат \U с кодовой точкой 0001F609.

Важно: В языке Go одинарные и двойные кавычки имеют разное значение. Одинарные кавычки используются для отдельных символов (литералов), а двойные — для объявления строк. Поэтому при работе с типами byte и rune необходимо использовать только одинарные кавычки, иначе возникнет ошибка компиляции.

Проверка знаний

Теперь закрепим пройденный материал. Создайте файл rune.go и напишите код, в котором переменной a присваивается шестнадцатеричное значение 0x1F648, после чего программа должна корректно вывести этот символ.

  • Требования: Файл rune.go должен находиться в директории ~/project.
  • Подсказка: Для длинных шестнадцатеричных кодов Unicode необходимо использовать специальный формат записи.
package main

import "fmt"

func main() {
    var a rune = 0x1F648
    fmt.Printf("The value of a is: %c\n", a)
}

Резюме

Давайте подведем итоги того, что мы изучили в этом разделе:

  • Символы ASCII занимают один байт и включают 128 знаков.
  • Кодировка UTF-8 — это реализация Unicode с переменной длиной символа. Количество байтов зависит от конкретного знака.
  • Тип данных byte используется для работы с символами ASCII, в то время как rune предназначен для полноценной поддержки Unicode.
  • Тип byte технически идентичен uint8, а runeint32.

В ходе этой лабораторной работы мы разобрали основы ASCII, UTF-8 и Unicode, а также выяснили, как символьные типы byte и rune соотносятся с целочисленными типами данных в Go.

✨ Проверить решение и практиковаться