Перейти к содержанию
Innovialabs

Unicode и UTF-8: как работают кодировки и откуда «кракозябры»

Практика3 мин

Коротко

Unicode присваивает каждому символу номер, а UTF-8 превращает этот номер в 1–4 байта. «Кракозябры» появляются, когда байты записали в одной кодировке, а прочитали в другой, — поэтому везде явно указывайте UTF-8.

Коротко: откуда берутся «кракозябры»

Компьютер хранит не буквы, а байты. Кодировка — правило, по которому байты превращаются в символы. Если текст сохранили в одной кодировке, а открыли в другой, вместо «Привет» вы увидите «РџСЂРёРІРµС‚».

Решение почти всегда одно: использовать UTF-8 везде и явно указывать кодировку при чтении и записи.

Unicode, кодовые точки и UTF-8

  • Unicode — большая таблица, где у каждого символа есть номер, кодовая точка. Кириллическая «А» — U+0410, латинская a — U+0061, узбекский знак ‘ в o‘ — U+2018.
  • UTF-8 — способ записать кодовую точку байтами. Латиница занимает 1 байт, кириллица — 2, многие знаки препинания и символы — 3, эмодзи — 4.
СимволКодовая точкаБайты UTF-8
aU+006161
А (кириллица)U+0410D0 90
‘U+2018E2 80 98

Windows-1251 — старая однобайтная кодировка для кириллицы. В ней «А» — это один байт C0. Она до сих пор встречается в выгрузках из старых систем, CSV из Excel и устаревших базах.

Как появляется искажение

Возьмём «П»: в UTF-8 это байты D0 9F. Программа, считающая файл Windows-1251, видит два отдельных символа: D0 — «Р», 9F — «џ». Отсюда «Рџ» в начале испорченного слова.

Обратная ситуация: файл в Windows-1251 открыли как UTF-8. Такие байты не образуют корректных последовательностей, и вместо текста появляются знаки �.

Как исправить на практике

Читайте и пишите файлы с явной кодировкой:

with open("data.csv", encoding="utf-8") as f:
    text = f.read()

Конвертируйте старые файлы:

iconv -f WINDOWS-1251 -t UTF-8 old.csv > new.csv

Почините уже испорченную строку, если знаете, как она сломалась:

broken = "Привет"
fixed = broken.encode("cp1251").decode("utf-8")  # "Привет"

Проверьте каждый слой:

  • HTML: <meta charset="utf-8"> и заголовок Content-Type: text/html; charset=utf-8.
  • MySQL: кодировка utf8mb4, а не utf8 — последняя хранит максимум 3 байта и не принимает эмодзи.
  • PostgreSQL: база в кодировке UTF8.
  • CSV для Excel: сохраняйте в UTF-8 с BOM (encoding="utf-8-sig" в Python), иначе Excel может открыть кириллицу неправильно.
  • Редактор кода и Git: файлы в UTF-8.

Узбекские o‘ и g‘: отдельная ловушка

Апостроф в o‘, g‘ и в словах вроде ma’lumot можно набрать разными символами: ' (U+0027), ` (U+0060), ‘ (U+2018), ’ (U+2019), ʻ (U+02BB). Внешне они похожи, но для компьютера это разные символы. Последствия:

  • поиск по «o‘zbek» не находит «o'zbek»;
  • дубли в справочниках, клиентах, адресах;
  • URL и slug получаются разными.

Что делать: выберите один вариант для проекта и нормализуйте ввод при сохранении — заменяйте остальные варианты на выбранный. Для поиска храните дополнительное нормализованное поле.

Ещё одна деталь: длина строки. "o‘" — это 2 кодовые точки, но 4 байта в UTF-8. Если поле в базе ограничено байтами, а не символами, текст может обрезаться посреди символа.

Нормализация Unicode

Некоторые буквы можно записать двумя способами: «й» — одной кодовой точкой или «и» плюс комбинируемый знак. Выглядит одинаково, а сравнение строк даёт False. Приводите текст к одной форме:

import unicodedata
clean = unicodedata.normalize("NFC", text)

Частые вопросы

Нет. Для нового кода, баз и API используйте UTF-8. Windows-1251 нужна только для чтения старых данных и интеграции с системами, которые не умеют другого, — конвертируйте на входе.

Поможем с этим

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

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.