Почему одинаково выглядящий текст не одинаков
В Unicode более 150 000 символов, и многие из них невидимы или выглядят в точности как другие. Строка, скопированная с веб-страницы, из PDF или мессенджера, может содержать пробелы нулевой ширины, неразрывные пробелы, мягкие переносы, маркеры направления письма и маркер порядка байтов — ничего из этого не видно на экране, но всё это ломает сравнения, входы в систему и поиск в базе данных. Двойники ещё хуже: кириллическая а (U+0430) в большинстве шрифтов попиксельно идентична латинской a (U+0061), на чём основаны омографические фишинговые домены и тикеты «почему этого имени пользователя не существует».
Что показывает инспектор
- Кодовая точка (
U+00E9): номер, который присваивает Unicode. Один видимый символ может быть одной кодовой точкой (éв составной форме) или двумя (e+ комбинируемый акут) — в этом и есть различие NFC/NFD. - Байты UTF-8: 1–4 байта на кодовую точку; то, что хранится в файлах, базах данных и HTTP. Единицы UTF-16: то, что считают строки JavaScript и Java; эмодзи занимают две.
- Графемы: то, что человек воспринимает как один символ. Эмодзи-флаг — одна графема, две кодовые точки, восемь байт UTF-8.
Нормализация
NFC объединяет базовый символ + знак в одну кодовую точку, если такая существует; NFD разлагает. Сравнивайте и храните текст в NFC (имена файлов из macOS приходят в NFD — классическая ошибка синхронизации). NFKC и NFKD дополнительно сворачивают символы совместимости: лигатуры, полноширинные буквы, надстрочные знаки и римские цифры становятся своими простыми эквивалентами — это нужно для поиска и идентификаторов, но уничтожает форматирование.
Очистка ввода
Для идентификаторов, имён пользователей и доменов: нормализуйте в NFKC, удалите символы, которые этот инструмент помечает как невидимые, и отклоняйте или предупреждайте при смешении письменностей. Опция «скрыть невидимые» даёт очищенную строку для копирования.
Вопросы
Что такое пробел нулевой ширины?
U+200B, символ без ширины, обозначающий возможное место переноса строки. Он переживает копирование-вставку и невидим почти в любом редакторе. Его вставляют текстовые процессоры, Slack и некоторые веб-страницы; этот инспектор помечает каждый.
Почему «é» иногда считается за два символа?
Её можно записать как одну кодовую точку U+00E9 или как U+0065 e, за которой следует U+0301 комбинируемый акут. Они выглядят одинаково, но при сравнении не равны, если обе не нормализованы в NFC.
Как ввести символ по кодовой точке?
Linux (GTK): Ctrl+Shift+U, затем hex, затем пробел. macOS: включите источник ввода Unicode Hex Input и удерживайте Option. Windows: введите hex, затем Alt+X в Word и WordPad. В коде: "\u{1F600}" (JS), "\N{...}" или "\U0001F600" (Python).
В чём разница между UTF-8 и Unicode?
Unicode — каталог символов и их номеров. UTF-8 (а также UTF-16, UTF-32) — способ записи этих номеров байтами. UTF-8 — тот, что используется повсюду в вебе.