Почему похожие строки не совпадают
На вид строки одинаковы, но отличаются буквой, пробелом или знаком.

На вид строки одинаковы, но отличаются буквой, пробелом или знаком.

Найти причину можно без изучения всей таблицы Unicode. Сначала определите строку, которая не совпала, затем проверьте подозрительную букву, внутренний пробел или букву из нескольких частей и исправьте текст по правилам конкретного поля.
В статье вы узнаете:
В старой выгрузке магазина есть артикул A-120. В новой выгрузке находится с виду тот же А-120, но сравнение двух списков помещает первую запись в блок «Только в первом списке», а вторую в блок «Только во втором списке». Выбран режим «Не учитывать регистр», пробелов по краям нет. Кажется, калькулятор упрямится.
Разница спрятана в первой букве. В старом артикуле стоит латинская A, в новом кириллическая А. У каждой есть собственный номер в таблице символов Unicode: латинская называется U+0041, кириллическая U+0410. Запись U+ просто сообщает номер знака. В выбранном шрифте рисунки почти совпали, но для программы это два разных элемента.
Проверка занимает несколько секунд. Если вставить латинскую A в конвертер ASCII-кодов, получится число 65. Кириллическая А в эту базовую таблицу латинских знаков не входит, поэтому инструмент остановится и назовёт U+0410.
Если правило артикулов разрешает только латинские буквы, новый вариант нужно исправить и повторить сравнение. В обычном имени или русском предложении та же кириллическая «А» совершенно правильна. Ошибка находится не в букве, а в её несоответствии договорённости для конкретного поля.
Буквы из разных алфавитов. Латинские A, B, C, E, H, K, M, O, P, T, X похожи на некоторые заглавные буквы кириллицы. Список здесь нужен только как предупреждение, а не как команда заменять всё подряд. Латинское P в коде товара может быть обязательным, а кириллическая «Р» в фамилии Романов должна остаться кириллической.
Пробелы с разными свойствами. Обычный пробел имеет номер U+0020. Неразрывный пробел U+00A0 выглядит примерно так же, но не разрешает разрывать строку между соседними словами. Фразы Иван Петров и Иван Петров содержат по 11 видимых позиций, поэтому простой взгляд и даже длина строки не всегда помогают. URL-кодировщик покажет обычный пробел как %20, а неразрывный как %C2%A0.
Многие программы автоматически убирают пустое место в начале и конце строки, но сохраняют знак между словами. Копирование из сайта, PDF или оформленного документа способно принести неразрывный пробел вместе с текстом.
Одна буква из нескольких частей. Русская «ё» может храниться готовым знаком U+0451. Другой вариант состоит из буквы «е» U+0435 и отдельного знака двух точек U+0308. Браузер рисует обе части в одном месте, поэтому человек видит одну «ё», а простое сравнение получает одну часть в первой строке и две во второй.
Счётчик символов покажет для готовой «ё» длину 1, а для сочетания «е» с отдельными точками длину 2, потому что текущий инструмент считает технические части, которыми браузер хранит строку. URL-кодировщик тоже выдаст разные записи: %D1%91 и %D0%B5%CC%88. Это не две орфографии слова. Это два способа сохранить одну и ту же букву.
Нормализация Unicode приводит эквивалентные последовательности к согласованной форме. Один распространённый вариант называется NFC. Для нашего примера он соберёт е и отдельные две точки в готовую ё. После NFC обе строки получат одинаковую внутреннюю запись и смогут совпасть при обычном сравнении.
Такое преобразование описывает Unicode Standard Annex #15. Стандарт задаёт четыре формы нормализации. Для обычного хранения и сравнения текста часто выбирают NFC, но форму должен определять разработчик системы. Пользователю важнее практический вывод: одинаковые буквы, собранные разными способами, можно привести к одному виду без ручного перебора.
Латинская A и кириллическая А нормализацией не склеятся. Они обозначают буквы разных письменностей и могут иметь разные правила регистра, произношение и назначение. То же относится к латинской C и кириллической «С». Похожий рисунок не делает знаки эквивалентными.
Регистр тоже обрабатывается отдельно. Строки Box и box могут считаться равными в списке имён и разными в техническом ключе. Удаление пробелов по краям является ещё одним отдельным решением. Одна команда «почистить текст» на самом деле скрывает несколько правил, и каждое нужно выбирать по смыслу данных.
Для бытовой сверки не нужен экран, заполненный шестнадцатеричными числами. Достаточно идти от общего различия к конкретному месту:
A и А обе занимают одну позицию.%XX подтвердят, что внутри находятся разные байты UTF-8.Этот путь показывает класс проблемы, но не рисует точный посимвольный diff. В длинной строке после первого подтверждения удобнее включить показ невидимых знаков в редакторе или использовать Unicode-инспектор, который выводит номер каждого символа. Не пытайтесь искать отличие, меняя буквы наугад: так легко починить артикул и одновременно испортить соседнее имя.
Для артикула A-120 можно установить простой договор: латинские буквы A-Z, цифры и дефис. Тогда кириллическая А отклоняется при вводе, а человек видит понятное сообщение. Это надёжнее, чем принять любой знак, а потом незаметно угадать, что он «наверное имел в виду».
С именами и обычным текстом подход другой. Кириллица, латиница, дефисы и знаки с диакритикой могут быть правильной частью записи. Здесь разумно сохранить оригинал, привести равнозначные составные буквы к NFC и отдельно договориться о пробелах. Автоматически переводить всё в один алфавит нельзя.
Внутренние пробелы тоже нельзя просто удалить. Новая папка без пробела превращается в другое название, а Иван Петров в слитную запись. Для сравнения списков полезно заменить несколько соседних пробелов одним только там, где формат требует ровно один разделитель.
Поиск по каталогу может игнорировать регистр и часть пунктуации, а уникальный код заказа должен оставаться строгим. Пароли и уже выданные идентификаторы нельзя незаметно переписывать новым правилом: там каждый знак является частью точного значения.
Похожие знаки используют не только по ошибке. Строка paypal может быть записана с кириллическими «а», и в некоторых шрифтах подмена почти незаметна. Стандарт Unicode UTS #39 называет такие строки смешиваемыми и приводит именно пару с латинским и смешанным написанием paypal как пример.
Таблица похожих символов помогает заметить риск, но сам стандарт предупреждает: её нельзя использовать как автоматическую нормализацию идентификаторов. Если программа молча превратит любой похожий знак в латинский, она может склеить разные имена и скрыть подмену вместо того, чтобы показать её.
Смешение алфавитов не доказывает обман. В названии, научной записи или многоязычном тексте оно бывает законным. Но в адресе сайта, логине, артикуле и другом коротком идентификаторе неожиданная буква из другого алфавита является поводом остановиться и проверить источник.
Практическое правило получается скучным, зато рабочим. Для свободного текста сохраняйте буквы и нормализуйте только эквивалентные формы по заранее выбранному правилу. Для идентификаторов задавайте допустимый алфавит и отклоняйте неожиданное. Если две строки вдруг не совпали, не ругайте калькулятор: возможно, он первым заметил букву, которую глаз вежливо пропустил.
Напишите своё мнение, комментарий или предложение.