# Unicode и UTF-8

> Unicode — стандарт, присваивающий уникальный номер каждому символу всех письменностей мира; UTF-8 — самая распространённая кодировка, записывающая эти номера последовательностями от 1 до 4 байтов.

Источник: https://llmdocs.ru/wiki/unicode-utf8/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Unicode**: стандарт кодирования символов, Консорциум Юникода (с 1991)
- **Кодовое пространство**: U+0000 — U+10FFFF (1 114 112 кодовых точек)
- **Число символов**: более 150 тысяч
- **Кодировки**: UTF-8, UTF-16, UTF-32
- **UTF-8**: Кен Томпсон и Роб Пайк (1992), RFC 3629
- **Доля веб-сайтов в UTF-8**: около 98%
- **Совместимость**: первые 128 символов совпадают с ASCII
**Unicode** (Юникод) — международный стандарт, который присваивает уникальный номер — **кодовую точку** — каждому символу всех письменностей мира: латинице, кириллице, иероглифам, арабскому письму, математическим знакам, эмодзи. **UTF-8** — самая распространённая **кодировка** Unicode, то есть способ записать эти номера байтами: от одного байта для латиницы до четырёх для эмодзи. Сегодня в UTF-8 работает около 98% веб-сайтов,[7] и это кодировка по умолчанию в [Linux](https://llmdocs.ru/wiki/linux.md), [Python](https://llmdocs.ru/wiki/python.md), [Go](https://llmdocs.ru/wiki/go.md), [Rust](https://llmdocs.ru/wiki/rust.md), [JSON](https://llmdocs.ru/wiki/json.md) и [HTML](https://llmdocs.ru/wiki/html.md).

Unicode решил проблему, мучившую программистов десятилетиями: текст, набранный на одном компьютере, превращался в «кракозябры» на другом.

## История

Первые кодировки, например ASCII (1963), описывали 128 символов — латиницу, цифры и знаки препинания. Для других языков создавали 8-битные расширения, и для одного только русского их было несколько: КОИ-8, CP866 (DOS), Windows-1251, ISO 8859-5. Текст в одной кодировке, прочитанный в другой, превращался в бессмыслицу вроде «Ïðèâåò» — по-японски это называют *mojibake*.

В 1991 году Консорциум Юникода выпустил версию 1.0 стандарта с единым набором символов для всех языков.[1] Сначала предполагалось, что хватит 65 536 символов и 16 бит на каждый, но вскоре пространство расширили до 1 114 112 кодовых точек. Новые версии выходят ежегодно и добавляют письменности и эмодзи; сейчас в стандарте более 150 тысяч символов.

В сентябре 1992 года Кен Томпсон и Роб Пайк придумали UTF-8 — по легенде, за один вечер в закусочной в Нью-Джерси — и в течение недели перевели на неё операционную систему Plan 9.[5] Стандарт RFC 3629 (2003) ограничил UTF-8 четырьмя байтами на символ.[2]

## Кодовые точки и кодировки

Кодовая точка записывается как `U+` и шестнадцатеричный номер: `U+0041` — латинская «A», `U+0416` — «Ж», `U+1F600` — 😀. Unicode определяет символы и их свойства, но не то, как хранить номера в байтах, — это делают кодировки:

| Кодировка | Байтов на символ | Особенности |
|---|---|---|
| UTF-8 | 1–4 | совместима с ASCII, нет проблем с порядком байтов; стандарт веба и Unix |
| UTF-16 | 2 или 4 | внутреннее представление строк в Windows, Java, JavaScript |
| UTF-32 | 4 | фиксированная длина, расточительна по памяти |

## Как устроена UTF-8

| Диапазон | Байты | Шаблон |
|---|---|---|
| U+0000 — U+007F | 1 | `0xxxxxxx` — ASCII без изменений |
| U+0080 — U+07FF | 2 | `110xxxxx 10xxxxxx` — кириллица, греческий, иврит |
| U+0800 — U+FFFF | 3 | `1110xxxx 10xxxxxx 10xxxxxx` — большинство иероглифов |
| U+10000 — U+10FFFF | 4 | `11110xxx 10xxxxxx 10xxxxxx 10xxxxxx` — эмодзи, редкие символы |

Схема удачна сразу в нескольких отношениях: любой ASCII-текст уже является корректной UTF-8, по первому байту видно длину символа, а с любого места потока можно найти начало следующего символа. Русская буква в UTF-8 занимает два байта, поэтому русский текст вдвое «тяжелее» английского по байтам — это же влияет и на [токенизацию](https://llmdocs.ru/wiki/tokenization.md) в языковых моделях.

```python
s = "Привет, мир! 👋"
print(len(s))                    # 14 — кодовых точек
print(len(s.encode("utf-8")))    # 26 — байтов: кириллица по 2, эмодзи 4
print(s.encode("utf-8")[:4])     # b'\xd0\x9f\xd1\x80' — «Пр»
print(hex(ord("Ж")), chr(0x1F600))
```

## Что считать символом

То, что пользователь видит как один символ, может состоять из нескольких кодовых точек:

- «й» можно записать одной точкой `U+0439` или двумя: «и» + комбинируемая кратка `U+0306`. Выглядят одинаково, но строки не равны.
- Эмодзи семьи 👨‍👩‍👧 — несколько эмодзи, соединённых невидимым символом.
- Флаги стран — пары специальных символов-букв.

Поэтому длина строки в кодовых точках не совпадает с числом видимых символов — **графемных кластеров**, правила разбиения на которые описаны в приложении UAX #29.[4] Для корректного сравнения строки **нормализуют** к единой форме (NFC — составные символы, NFD — разложенные), иначе поиск и сравнение имён файлов дают неожиданные результаты.[3]

## Практические правила

1. **Везде UTF-8**: файлы исходного кода, базы данных, HTTP-ответы (`Content-Type: text/html; charset=utf-8`), терминал.
2. **Декодировать на входе, кодировать на выходе**: внутри программы работать со строками, а байты — только на границах (файлы, сеть).
3. **Указывать кодировку явно** при открытии файлов: `open(path, encoding="utf-8")` — иначе на Windows может использоваться системная кодировка.
4. **Не резать строки по байтам**: обрезка посередине многобайтного символа ломает текст.
5. **Не добавлять BOM** (маркер порядка байтов) в UTF-8 без необходимости — он ломает скрипты с `#!` и некоторые парсеры.
6. В [PostgreSQL](https://llmdocs.ru/wiki/postgresql.md) создавать базы с кодировкой UTF8, в MySQL использовать `utf8mb4`, а не урезанную `utf8`, которая не вмещает эмодзи.

Подробное руководство по работе с Unicode в Python — в официальной документации.[6]

## См. также

- [Токен и токенизация](https://llmdocs.ru/wiki/tokenization.md)
- [Регулярные выражения](https://llmdocs.ru/wiki/regex.md)
- [Python](https://llmdocs.ru/wiki/python.md)
- [JSON](https://llmdocs.ru/wiki/json.md)
- [HTML](https://llmdocs.ru/wiki/html.md)

## Источники

1. [The Unicode Standard](https://www.unicode.org/standard/standard.html)
2. [RFC 3629 — UTF-8, a transformation format of ISO 10646](https://www.rfc-editor.org/rfc/rfc3629)
3. [Unicode Standard Annex #15 — Unicode Normalization Forms](https://www.unicode.org/reports/tr15/)
4. [Unicode Standard Annex #29 — Unicode Text Segmentation](https://www.unicode.org/reports/tr29/)
5. [Rob Pike — UTF-8 history](https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt)
6. [Python — Unicode HOWTO](https://docs.python.org/3/howto/unicode.html)
7. [W3Techs — Usage statistics of character encodings for websites](https://w3techs.com/technologies/overview/character_encoding)
