Когда Base64 «нарушает китайский язык», проблема обычно заключается в границе между символами и байтами. Base64 не знает, что последовательность представляет собой китайский текст или эмодзи. Он сохраняет байты; приложение выбирает, как эти байты создаются и интерпретируются.
Следуйте за байтами
Символ 中 — это U+4E2D. В UTF-8 используются байты E4 B8 AD, представление Base64 которого равно 5Lit. Другая кодировка символов создает разные байты и, следовательно, другое значение Base64. Согласуйте UTF-8 на обоих концах, прежде чем сравнивать закодированные строки.
const bytes = new TextEncoder().encode('中');
console.log([...bytes]); // [228, 184, 173]
console.log([...bytes].map(b => b.toString(16)).join(' ')); // e4 b8 ad
Эмодзи иллюстрируют еще одно различие: JavaScript. '😊'.length представляет собой две кодовые единицы UTF-16, а его представление UTF-8 имеет четыре байта. Ни одно из чисел само по себе не является количеством видимых, воспринимаемых пользователем символов в произвольном тексте. Используйте длину в байтах для вычислений файлов и транспорта.
Сделать замену видимой
Символ замены � может означать, что более ранний декодер уже потерял информацию. После замены неизвестных байтов в строке и повторного кодирования этой строки исходные значения обычно невозможно восстановить. Проверьте необработанный декодированный массив байтов перед дальнейшими преобразованиями.
const decoder = new TextDecoder('utf-8', { fatal: true });
console.log(decoder.decode(Uint8Array.of(0xe4, 0xb8, 0xad))); // 中
try { decoder.decode(Uint8Array.of(0xff)); }
catch { console.log('Invalid UTF-8; preserve the original byte.'); }
Нормализация — это отдельная операция.
Две строки могут выглядеть одинаково, но содержат разные последовательности Юникода. Буква с ударением é может представлять собой один заранее составленный символ или букву e, за которой следует комбинированный акцент. UTF-8 и Base64 правильно сохраняют эту разницу. Если вашему приложению требуется нормализованный текст, явно выберите форму нормализации и примените ее перед кодированием; никогда не называйте измененный результат восстановлением с сохранением байтов.
Также проверьте невидимые символы, окончания строк и возможную метку порядка байтов, если результаты различаются. Это настоящие входные байты, а не обязательно ошибки кодировщика. Для подписанных сообщений и контрольных сумм даже невидимая разница меняет результат.
Надежный порядок устранения неполадок: сохраните источник, проверьте байты, определите ожидаемую кодировку символов, декодируйте с явной обработкой ошибок и только затем приступайте к нормализации. МДН: btoa() объясняет, почему прямые вызовы btoa браузера не подходят для произвольного текста в Юникоде. Двоичные файлы должны полностью игнорировать декодирование текста.