Что такое кодировки символов? Чем отличаются ASCII, UTF-8, UTF-16? <!-- grade: junior -->
Кодировка символов — это набор правил, определяющих, как символы (буквы, цифры, знаки) представляются в виде последовательности байтов в памяти компьютера.
Аналогия: кодировка — это словарь-переводчик между человеческими буквами и числами. ASCII — маленький англо-числовой словарь на 128 слов. Unicode — полная энциклопедия всех языков мира. А UTF-8 и UTF-16 — разные способы записать слова из этой энциклопедии на бумаге.
ASCII
ASCII (American Standard Code for Information Interchange):
- 7-битная кодировка, 128 символов (0-127).
- Включает: латинские буквы (A-Z, a-z), цифры (0-9), знаки препинания, управляющие символы (перенос строки, табуляция).
- 1 символ = 1 байт.
- Не поддерживает кириллицу и большинство других алфавитов.
Unicode
Unicode — универсальный стандарт, присваивающий уникальный номер (code point) каждому символу всех языков мира:
- Содержит более 150 000 символов.
- Code point записывается как
U+XXXX, например:U+0041= ‘A’,U+0410= ‘А’ (кирилл.). - Unicode — это не кодировка, а набор символов (character set). Кодировки UTF-8, UTF-16, UTF-32 определяют, как code points хранятся в байтах.
UTF-8
UTF-8 (Unicode Transformation Format, 8-bit):
- Кодировка переменной длины: 1-4 байта на символ.
- Совместима с ASCII: символы 0-127 занимают 1 байт (те же значения, что в ASCII).
- Латиница — 1 байт, кириллица — 2 байта, иероглифы — 3 байта, эмодзи — 4 байта.
- Самая распространённая кодировка в Интернете (>98% веб-страниц).
- Используется по умолчанию в Linux, JSON, XML, HTML5.
UTF-16
UTF-16:
- Кодировка переменной длины: 2 или 4 байта на символ.
- Большинство символов (включая кириллицу, латиницу, иероглифы) — 2 байта.
- Символы за пределами BMP (Basic Multilingual Plane), например эмодзи — 4 байта (суррогатная пара).
- Используется внутри Java:
Stringиcharоснованы на UTF-16.char= 2 байта = один элемент UTF-16.
Сравнение
| Свойство | ASCII | UTF-8 | UTF-16 |
|---|---|---|---|
| Размер символа | 1 байт | 1-4 байта | 2-4 байта |
| Совместимость с ASCII | Да | Да | Нет |
| Поддержка всех символов Unicode | Нет | Да | Да |
| Эффективность для латиницы | Максимальная | Максимальная | Низкая (2 байта на символ) |
| Эффективность для иероглифов | Не поддерживает | 3 байта | 2 байта |
| Использование | Устаревший стандарт | Веб, Linux, JSON, XML | Java, Windows, .NET |
Работа с кодировками в Java
Пример
String s = "Привет";
s.length(); // 6 — количество char (UTF-16 единиц)
s.codePointCount(0, s.length()); // 6 — количество Unicode символов (code points)
s.getBytes("UTF-8").length; // 12 — 6 кириллических символов x 2 байта UTF-8
s.getBytes("UTF-16").length; // 14 — 6 x 2 + 2 (BOM)
Важный нюанс: для символов за пределами BMP (например, эмодзи) String.length() вернёт 2 (суррогатная пара из двух char), хотя это один символ. Поэтому для корректного подсчёта символов используйте codePointCount().
Начиная с Java 9, строки внутри JVM могут храниться в Compact Strings: если все символы входят в Latin-1 (1 байт), используется byte[] вместо char[], экономя память вдвое для ASCII-строк.
Вывод
ASCII — базовая однобайтовая кодировка для латиницы. Unicode — универсальный набор символов, а UTF-8 и UTF-16 — способы его кодирования в байты. UTF-8 доминирует в вебе, UTF-16 используется внутри Java. Ключевой момент: String.length() в Java возвращает количество char (UTF-16 единиц), а не количество символов.
На собеседовании: junior-вопрос. Часто спрашивают: почему
"Hello".getBytes("UTF-8").lengthотличается от"Привет".getBytes("UTF-8").length? Ответ: кириллица в UTF-8 занимает 2 байта на символ. Также важно понимать ловушку сlength()для эмодзи и суррогатных пар.