Раздел 41 · Zig с нуля

Строки и работа с UTF-8

junior~10 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Строки и работа с UTF-8

В коробке лежат карточки, а на каждой буква. Если буква крупная, её могут напечатать на нескольких кусочках. Подсчёт кусочков не равен подсчёту букв. В UTF-8 одна кодовая точка занимает от одного до четырёх байтов, поэтому длина массива байтов и длина текста отвечают на разные вопросы.

Возьмём слово «кот». На экране три буквы, но в памяти UTF-8 шесть байтов. Сегодня научимся не смешивать эти величины.

Одна мысль урока

Строка Zig хранит последовательность байтов; смысл UTF-8 нужно учитывать отдельно.

Опора из прошлого урока: Метапрограммирование без макросов.

От картинки к записи

UTF-8

задаёт кодировку, то есть договор о преобразовании текста в байты. Обычная строка Zig часто имеет вид []const u8. Тип не обещает, что любые байты внутри образуют правильный UTF-8. Файл или сетевой пакет может содержать произвольные данные.

Кодовая точка

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

Строковый литерал "кот" записывается исходником в UTF-8. Его .len сообщает число байтов: шесть. Индексация [0] читает один байт, не букву к целиком. Разрезание строки по произвольному байтовому индексу может попасть внутрь многобайтной последовательности и сделать две невалидные части.

std.unicode.utf8ValidateSlice проверяет корректность байтов. std.unicode.utf8CountCodepoints считает кодовые точки и возвращает ошибку для неправильной последовательности. try здесь честно отражает возможный неуспех. Если данные пришли извне, нужно выбрать, возвращать ошибку пользователю или обрабатывать повреждённый текст другим объявленным способом.

Для равенства строк используем std.mem.eql(u8, a, b), а в тестах expectEqualStrings. Сравнение срезов не следует воспринимать как готовое сравнение их содержимого оператором ==. Последовательности могут лежать по разным адресам и содержать одинаковые байты. Смысл задачи обычно требует сравнивать именно содержимое.

Строковый литерал также имеет нулевой завершитель для удобства некоторых внешних API. Но обычный срез описывает полезные байты длиной, не обязательно включая завершитель. Ноль внутри произвольного среза остаётся байтом. Для C-строк есть особый договор о первом нуле, который разберём в уроке про C.

Изменяемый []u8 не делает операции с текстом автоматически безопасными. Можно заменить один ASCII-байт другим и сохранить длину, но вставка кириллической буквы может требовать другого числа байтов. Поэтому не обещай «заменить любую букву через один индекс». Сначала выбери уровень работы: байты, кодовые точки или видимые знаки.

Не нормализуем текст в этом уроке. Две похожие визуально строки могут иметь разные последовательности Unicode. Для поиска и сравнения по человеческим правилам нужны дополнительные решения. Первое полезное умение проще: назвать, что именно измеряет .len, и не разрезать UTF-8 внутри кодовой точки.

премиум-урок

Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.

Оплата пока не подключена: премиум и курсы открываются промокодом.