Что такое текст на самом деле и почему инструменты в нём расходятся
Текстовый файл — это байты плюс кодировка, а кодировка в файле не хранится. Только соглашение сообщает программе, что определённый байт означает «é», — поэтому один и тот же файл в одном месте открывается правильно, а в другом превращается в «Ã©». UTF-8 — современное решение: обычные латинские буквы он хранит в одном байте, буквы с диакритикой — в двух, а большую часть китайского, японского, арабского и все эмодзи — в трёх или четырёх. UTF-16 используют внутри Windows и JavaScript: почти всё хранится в двух байтах, остальное — в четырёх. Windows-1252 и Latin-1 — однобайтовые кодировки, которые до сих пор выдают старые системы, и обычно именно они порождают «кракозябры». Что полезно знать об этих страницах: «Просмотр CSV» определяет кодировку добавленного файла, включая UTF-16 и Windows-1252, и позволяет её сменить. Простые текстовые поля этого не делают — перетащенный файл читается как UTF-8, поэтому в экспорте в Latin-1 буквы с диакритикой отобразятся неправильно, и решение — сначала его конвертировать.
У слова «символ» четыре значения, и нужное зависит от того, кто устанавливает ограничение. То, что человек видит как один символ, — это кластер графем. То, чему соответствует регулярное выражение, — кодовая точка. То, что возвращает string.length в JavaScript, — единицы UTF-16. То, что измеряет столбец базы данных или HTTP-заголовок, — байты UTF-8. Для простого английского текста все четыре совпадают, поэтому никто и не замечает, — а потом эмодзи «семья» оказывается одной графемой, семью кодовыми точками, одиннадцатью единицами UTF-16 и двадцатью пятью байтами, и сообщение из 200 символов не проходит в поле на 255 символов. Именно поэтому «Счётчик символов» показывает все четыре значения.
Невидимые символы — настоящие символы. Неразрывный пробел выглядит точно так же, как обычный, но это совершенно другой символ — он постоянно попадает в текст, скопированный с веб-страницы или из текстового редактора, и ломает поиск, разбор CSV и код, который разбивает строку по пробелам. Соединители нулевой ширины удерживают вместе эмодзи с несколькими людьми. Мягкие переносы, метки направления текста и метки порядка байтов тоже путешествуют вместе со вставленным текстом. Ничего из этого на экране не видно, поэтому единственный сигнал — счёт, который не совпадает с тем, что вы видите, и это одно из честных применений счётчика символов. Учтите, что параметр «Не учитывать все пробелы» в «Сравнить тексты» схлопывает обычные пробелы и табуляции, но не считает неразрывный пробел пробелом, потому что это не одно и то же.
Концы строк — причина, по которой сравнение иногда показывает изменёнными все строки. Windows заканчивает строку возвратом каретки и переводом строки, все остальные — только переводом строки. Сохраните файл с LF в редакторе на Windows — и каждая строка теперь отличается на один невидимый байт, так что побайтовое сравнение — git diff, diff(1) — сообщит, что файл переписан целиком, и спрячет единственное изменение, которое вы на самом деле внесли. Наши «Сравнить тексты» и «Удалить повторяющиеся строки» перед сравнением разбивают текст по всем трём соглашениям, поэтому файл, у которого поменялись только концы строк, не превращается здесь в сплошную красную стену. В инструменте для чтения это удобство, а в репозитории — ловушка: исправляйте это в источнике — в редакторе или в собственных настройках концов строк в git.
Две одинаковые на вид строки могут оказаться разными, и обычно дело в нормализации. В Unicode «é» можно записать двумя способами: одной кодовой точкой или обычной «e» с последующим комбинируемым акутом. Выглядят они одинаково, но это разные последовательности байтов, поэтому сравнение, удаление повторов или поиск в базе данных считают их двумя разными значениями. macOS и Windows исторически расходились в том, какую форму использовать для имён файлов, — так в списке, собранном с двух компьютеров, появляются на вид точные дубликаты, которые не удаляются. Заметить это поможет «Счётчик символов»: у двух форм одинаковое число графем и разное число кодовых точек. Ни один из этих инструментов не переводит одну форму в другую — это задача на одну строку для скрипта с библиотекой Unicode, например unicodedata.normalize в Python.
«Количество слов» — это суждение, а не измерение. Разбиение по пробелам — правило английского языка, и если применить его к китайскому, японскому или тайскому, где слова не разделяются пробелами, длинная статья окажется одним словом. Наш счётчик вместо этого использует встроенную в браузер сегментацию слов Unicode, которая знает, где начинаются слова в любой письменности. Остальные расхождения касаются дефисов («well-known» — одно слово здесь и в Word, два — в некоторых инструментах), отдельно стоящих чисел и того, что считать предложением: «We met Dr. Smith» — одно предложение, а наивный разбор насчитает два. Время чтения — ещё одна оценка поверх этого: 238 слов в минуту при чтении про себя, по данным метаанализа, а не круглое число, которое кочует из блога в блог.