Gymterview
junior

Что такое кодировки символов? Чем отличаются 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() для эмодзи и суррогатных пар.