Получить доступ

Чем Unicode отличается от UTF-8?

JuniorЧисла в компьютере
Ещё спрашивают так:
  • Что такое кодировка?
  • Почему длина строки с эмодзи бывает больше, чем видно на экране?
  • Чем UTF-8 отличается от UTF-16?
Коротко

Unicode — таблица: каждому символу номер (кодовую точку), U+0416 — «Ж». UTF-8 — способ записать эти номера байтами.

  • UTF-8: от 1 до 4 байт на символ, ASCII — 1 байт, кириллица — 2, эмодзи — 4
  • UTF-16: 2 или 4 байта (суррогатные пары) — внутри Java, JavaScript, Windows
  • «Длина строки» бывает в байтах, единицах кодировки, кодовых точках и видимых символах

А ещё Кракозябры — текст прочитали не в той кодировке, в которой записали

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Компьютер хранит байты, текст — договорённость, как их читать.

Unicode — единый каталог символов всех письменностей: каждому присвоен номер, кодовая точка, от U+0000 до U+10FFFF. Это ещё не байты.

Кодировка превращает номера в байты:

  • UTF-8 — переменная длина: U+0000…U+007F (ASCII) — 1 байт, дальше 2, 3 или 4. Старшие биты первого байта говорят длину последовательности, а продолжения начинаются с 10, поэтому по любому месту в потоке можно найти начало символа. Совместима с ASCII, не зависит от порядка байтов — стандарт для файлов, сети и веба;
  • UTF-16 — единицы по 2 байта; символы за пределами базовой плоскости (эмодзи, редкие иероглифы) — парой единиц, суррогатной парой. Так строки устроены внутри Java, JavaScript, C#, Windows API;
  • UTF-32 — всегда 4 байта, просто, но расточительно;
  • однобайтовые — cp1251, KOI8-R: 256 символов, только одна письменность.

Что такое «длина» — частый вопрос с подвохом:

  • байты — len("Ж".encode()) = 2;
  • единицы UTF-16 — "😀".length() в Java = 2;
  • кодовые точки — len("😀") в Python = 1;
  • графемы, видимые символы — флаг 🇷🇺 это две кодовые точки, а «семья» эмодзи — до семи, соединённых невидимым ZWJ. «й» можно записать одной точкой или «и» плюс комбинирующий знак — и строки не будут равны без нормализации (NFC).

На практике: всегда явно указывать кодировку при чтении файлов и в HTTP (Content-Type: text/html; charset=utf-8), в MySQL выбирать utf8mb4 (старый utf8 хранит максимум 3 байта и теряет эмодзи), обрезать строки по графемам, а не по байтам.

Пример

s = "Ж😀"
len(s)                       # 2 кодовые точки
s.encode("utf-8")            # b'\xd0\x96\xf0\x9f\x98\x80' — 2 + 4 байта
len(s.encode("utf-16-le"))   # 6 — «Ж» 2 байта, эмодзи суррогатной парой 4
"Привет".encode("utf-8").decode("cp1251")  # 'Привет' — кракозябры

Что хотят услышать

  • Разделение: таблица символов против способа кодирования в байты.
  • Переменная длина UTF-8 и совместимость с ASCII.
  • UTF-16 и суррогатные пары — почему length у эмодзи 2.
  • Разные смыслы длины строки и явная кодировка при вводе-выводе.

Частые ошибки

  • «Unicode — это кодировка в 2 байта».
  • Резать строку по байтам и получать половину символа.

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем