Инструменты разработчика · Кодер и декодер Base64
Декодирование Base64 в UTF-8 без кракозябр: atob и TextDecoder
· Как это работает
base64 кодирование Юникод
atob() возвращает байты, замаскированные под символы, поэтому после декодирования акцентированный текст выглядит сломанным. В этом посте показан правильный конвейер из Base64 в байты в текст UTF-8 и как распознать шаблон сбоя.
Ответ API, который декодируется как «Кафе» — конкретный признак моджибаке и два байта за двумя неправильными символами.
Строка Base64 Q2Fmw6k= декодируется в байты (67, 97, 102, 195, 169), которые представляют собой UTF-8 текст Café. Вставьте в наивный декодер (просто преобразование atob и строк), и на выходе часто будет Café, где каждое ударение заменяется двумя неправильными символами. Этот моджибаке происходит потому, что atob возвращает строку байтов (кодовые единицы 0–255), а не текст UTF-8. Байты 195 и 169 кодируют ударение é в UTF-8.
Обработка их так, как если бы они были отдельными символами Latin-1, дает шаблон моджибаке. Правильный конвейер — это atob (байты как строка), затем TextDecoder (интерпретирует байты как UTF-8), и возвращается исходный текст. Функция atob не нарушена; он предназначен для двоичных данных. Его имя происходит от ASCII-to-binary, а создаваемая им двоичная строка представляет собой последовательность кодовых единиц 0–255, каждая из которых представляет один байт.
Что на самом деле возвращает atob() — строка кодовых единиц 0–255, которые обозначают байты, а не декодированный текст.
Если вы передадите ему Q2Fmw6k= (стандартный Base64), он выведет строку, в которой каждый символ представляет собой один байт: кодовая единица 67, затем 97, затем 102, затем 195, затем 169. Если отобразить эту строку напрямую или интерпретировать как текст Latin-1, вы увидите искаженный вывод. Недостающий шаг — преобразование кодовых единиц в массив байтов, а затем декодирование массива как UTF-8.
Цикл charCodeAt восстанавливает байтовые значения: для каждого символа в выходных данных atob вызовите charCodeAt, чтобы получить кодовую единицу (номер 0–255), сохраните ее в Uint8Array. Как только массив байтов существует, перейдите в TextDecoder с кодировкой utf-8. TextDecoder считывает последовательность байтов и интерпретирует как текст UTF-8, объединяя последовательности байтов, такие как (195, 169), в отдельные символы, такие как é. Байты (67, 97, 102, 195, 169) превращаются в четырехсимвольную строку Café. Цикл намеренно скучный: читайте каждую возвращаемую единицу кода с помощью charCodeAt и присваивайте ее соответствующей позиции Uint8Array. Никакого решения по набору символов там не происходит. Единственная интерпретация происходит, когда TextDecoder получает этот массив и применяет UTF-8 с обработкой фатальных ошибок.
Превращение этой строки в Uint8Array — цикл charCodeAt и почему это копия байта, а не преобразование
Этот двухэтапный процесс — восстановление байтов, затем интерпретация UTF-8 — это то, что кодировщик и декодер Base64 выполняют внутри себя. Шаблон моджибаке является явным признаком этой ошибки. Если Café отображается как Café, вы видите интерпретацию Latin-1 UTF-8 байт. UTF-8 байт для é — это 0xC3 0xA9 (десятичные 195, 169). В латинице 1 кодовая единица 195 — это Ã, а кодовая единица 169 — ©.
Когда последовательность байтов UTF-8 считывается так, как если бы каждый байт был отдельным символом Latin-1, каждая многобайтовая последовательность UTF-8 создает неправильные символы замены. Если Café отображается как Caf, за которым следует заменяющий символ, или как Caf? или Caf плюс U+FFFD, вы видите другую ошибку: декодер не распознал последовательность байтов как допустимую UTF-8. Конкретный пример: Base64 SGVsbG8sIOS4lueVjCEg8J-Zgg== декодирует следующим образом.
TextDecoder и решение о кодировке — декодирование как UTF-8, и почему кодировка — это отдельный факт, который вы должны знать
atob создает двоичную строку с байтами (72, 101, 108, 108, 111, 44, 32, 228, 184, 180, 149, 140, 33, 32, 240, 159, 152, 130). Первые шесть байтов — ASCII: становятся Hello. Байты 228, 184, 180 представляют собой трехбайтовую последовательность UTF-8, представляющую символ CJK. Байты 149, 140 являются частью следующей последовательности. Полная последовательность включает четырехбайтовую последовательность эмодзи (240, 159, 152, 130) для последнего символа.
При правильной обработке с помощью TextDecoder все байты объединяются для создания исходного текста со смешанным алфавитом. Последовательности байтов UTF-8 имеют предсказуемую длину: байт, начинающийся с 0xxxxxxx, является однобайтовым ASCII; байт, начинающийся с 110xxxxxx, ожидает следующий байт, начинающийся с 10xxxxxx (всего два байта); байт, начинающийся с 1110xxxx, ожидает два следующих байта (всего три байта); Байт, начинающийся с 11110xxx, ожидает три следующих байта (всего четыре байта). Для смешанного образца CJK и эмодзи байтовое представление особенно полезно, поскольку интуиция ASCII больше не помогает. Каждому видимому символу принадлежит несколько байтов, а удаление одного байта превращает оставшуюся последовательность в недействительную UTF-8. Строгая расшифровка превращает этот сдвиг в именованный сбой, а не в правдоподобный ущерб.
Рабочий пример: декодирование строки Base64, содержащей CJK и эмодзи — байты, кодовые точки и финальная строка по сравнению с оригиналом.
Последовательность, начинающаяся с 1111110x, недействительна в UTF-8 (зарезервирована на будущее, не используется). Байт, начинающийся с 10xxxxxx, никогда не должен выступать в качестве ведущего байта; это продолжение. Если поток байтов нарушает правила, он недействителен UTF-8. TextDecoder с кодировкой utf-8 интерпретирует массив по этим правилам и успешно обрабатывает допустимые последовательности. В случае недействительного сообщает об ошибке.
Кодер и декодер Base64 использует TextDecoder с флагом строгого режима true. Это означает, что недопустимый UTF-8 вызывает ошибку, а не автоматически вставляет символы замены (U+FFFD). Если строка Base64 декодируется в недопустимые байты UTF-8, строгий режим выдаст ошибку вместо продолжения работы с искаженным текстом. Это выбор дизайна: двоичные полезные данные (изображения, ключи, сжатые данные) не являются текстом и не должны декодироваться как текст.
Распознавание шаблона: Ã, †и � — как отличить проблему Base64 от проблемы с кодировкой
Если попытаться декодировать JPEG как Base64, поток байтов не будет представлять действительный UTF-8, и строгое декодирование отклонит его. Инструмент предлагает шестнадцатеричное представление для таких полезных данных: вы можете видеть необработанные байты, не притворяясь, что это текст. Распознавание ошибок декодирования UTF-8 предполагает просмотр байтов в контексте. Являются ли они нечетными числами там, где ожидается многобайтовая последовательность?
Является ли первый байт потенциальной последовательности недействительным (начиная с 10xxxxxx)? Отсутствуют ли байты продолжения? Кнопка байтового вывода обеспечивает самое чистое ответвление в расследовании. Если появляется шестнадцатеричный код, но декодирование текста завершается неудачно, анализ Base64 выполнен успешно, а полезные данные либо двоичные, повреждены, либо закодированы с использованием другой кодировки. Изменение пунктуации Base64 не может исправить несоответствие кодировки после того, как правильные байты уже появились.
Что здесь не распространяется — полезные нагрузки UTF-16, двоичный вывод, например изображения, и обработка недопустимых байтов.
Шаблоны последовательны. Одиночный байт 0xFF никогда не является допустимым в UTF-8; он не может быть ASCII байтом (только 0–127 являются ASCII) и не может быть ведущим байтом (ведущие байты — 0xC0–0xFD, 0xFF зарезервирован). Одиночные суррогаты (концепция UTF-16) не могут появляться в UTF-8; если вы видите последовательность байтов 0xED 0xA0 0x80 (которая кодирует суррогатный U+D800 в стиле UTF-8), это недопустимый UTF-8.
Одним из исторических обходных путей является btoa(unescape(encodeURIComponent(text))). encodeURIComponent превращает cafe в %C3%A9 (процентное кодирование UTF-8 байт), unescape переупаковывает как кодовые единицы, btoa кодирует кодовые единицы. Это работает для большинства текстов, но ненадежно для одиночных суррогатов и трудночитаемо. Современный конвейер — TextEncoder в байты, затем в base64 — более понятен и стандартен. TextEncoder встроен во все современные браузеры и Node.js, что делает правильный выбор. UTF-16, устаревшие однобайтовые кодировки и произвольное содержимое файлов требуют декодера, выбранного для этих байтов, или программы просмотра, поддерживающей двоичные файлы. ToolAcre намеренно не угадывает среди них. Догадки могут превратить неверную последовательность в вводящий в заблуждение текст, в то время как шестнадцатеричный дамп сохраняет каждый байт для более поздней, обоснованной интерпретации.
Вывод: Base64 дает вам байты, UTF-8 дает вам текст — как кодировщик и декодер Base64 выполняют оба шага, чтобы декодированный текст точно соответствовал входным данным.
Если у вас есть строка Base64 и вам нужен текст UTF-8, выполните следующие действия: декодируйте Base64 в байты (используя atob или библиотеку декодирования base64), создайте Uint8Array из байтов, передайте массив в TextDecoder с кодировкой utf-8, прочитайте результат как строку.
Если входные данные представляют собой двоичные данные, а не текст, пропустите TextDecoder и проверьте байты напрямую. Кодер и декодер Base64 обеспечивает представление шестнадцатеричных байтов, сохраняя значения, которые строгое декодирование UTF-8 отклонит. Эта вилка является диагностической: успешные байты плюс неудавшийся текст означают, что синтаксический анализ Base64 сработал, в то время как полезные данные являются двоичными, поврежденными или закодированы с помощью кодировки, которую этот инструмент не угадывает.