Tiếng Việt

Công cụ dành cho nhà phát triển · Bộ mã hóa và giải mã Base64

Giải mã Base64 thành UTF-8 không cần mojibake: atob plus TextDecoding

· Cách thức hoạt động

base64 mã hóa unicode

Đường dẫn từ Base64 đến UTF-8 hiển thị lỗi mojibake
Hình minh họa vector ToolAcre gốc

atob() trả về các byte được ngụy trang dưới dạng ký tự, đó là lý do tại sao văn bản có dấu trông bị hỏng sau khi giải mã. Bài đăng này hiển thị quy trình chính xác từ Base64 đến byte đến văn bản UTF-8 và cách nhận biết kiểu lỗi.

Phản hồi API giải mã thành 'Café' — một triệu chứng mojibake cụ thể và hai byte đằng sau hai ký tự sai

Chuỗi Base64 Q2Fmw6k= giải mã thành byte (67, 97, 102, 195, 169), là UTF-8 văn bản Café. Dán vào bộ giải mã đơn giản (chỉ atob và chuyển đổi chuỗi) và đầu ra thường là Café, mỗi dấu được thay thế bằng hai ký tự sai. Mojibake này xảy ra do atob trả về chuỗi byte (đơn vị mã 0–255), chứ không phải văn bản UTF-8. Các byte 195 và 169 mã hóa é có dấu trong UTF-8.

Xử lý chúng như thể các ký tự Latin-1 riêng biệt sẽ tạo ra mẫu mojibake. Quy trình chính xác là atob (byte dưới dạng chuỗi), sau đó TextDecoding (hiểu byte dưới dạng UTF-8) và văn bản gốc sẽ quay lại. Chức năng atob không bị hỏng; nó được thiết kế cho dữ liệu nhị phân. Tên của nó xuất phát từ ASCII sang nhị phân và chuỗi nhị phân mà nó tạo ra là chuỗi các đơn vị mã 0–255, mỗi đơn vị đại diện cho một byte.

Những gì atob() thực sự trả về — một chuỗi đơn vị mã 0–255 đại diện cho byte, không phải văn bản được giải mã

Nếu bạn cung cấp dữ liệu Q2Fmw6k= (Base64 tiêu chuẩn), nó sẽ xuất ra chuỗi trong đó mỗi ký tự là một byte: đơn vị mã 67, rồi 97, rồi 102, rồi 195, rồi 169. Nếu hiển thị trực tiếp chuỗi đó hoặc hiểu dưới dạng văn bản Latin-1, bạn sẽ thấy kết quả đầu ra bị cắt xén. Bước còn thiếu là chuyển đổi đơn vị mã thành mảng byte rồi giải mã mảng dưới dạng UTF-8.

Vòng lặp charCodeAt phục hồi các giá trị byte: với mỗi ký tự trong đầu ra atob, hãy gọi charCodeAt để lấy đơn vị mã (số 0–255), lưu trữ trong Uint8Array. Sau khi tồn tại mảng byte, hãy chuyển tới TextDecoding với bộ ký tự utf-8. TextDecoding đọc chuỗi byte và diễn giải dưới dạng văn bản UTF-8, kết hợp các chuỗi byte như (195, 169) thành các ký tự đơn như é. Byte (67, 97, 102, 195, 169) trở thành chuỗi bốn ký tự Café. Vòng lặp có chủ ý nhàm chán: đọc từng đơn vị mã được trả về bằng charCodeAt và gán nó vào vị trí Uint8Array phù hợp. Không có quyết định thiết lập ký tự nào xảy ra ở đó. Cách giải thích duy nhất xuất hiện khi TextDecoding nhận được mảng đó và áp dụng UTF-8 với việc xử lý lỗi nghiêm trọng.

Biến chuỗi đó thành Uint8Array - vòng lặp charCodeAt và tại sao nó là bản sao byte chứ không phải chuyển đổi

Quy trình hai bước này—khôi phục byte, sau đó diễn giải UTF-8—là những gì bộ mã hóa và giải mã Base64 thực hiện nội bộ. Mẫu mojibake là dấu hiệu nhận biết lỗi này. Nếu Café xuất hiện dưới dạng Café, bạn sẽ thấy cách giải thích Latin-1 của UTF-8 byte. UTF-8 byte cho é là 0xC3 0xA9 (thập phân 195, 169). Trong tiếng Latin-1, đơn vị mã 195 là Ã ​​và đơn vị mã 169 là ©.

Khi chuỗi byte UTF-8 được đọc như thể mỗi byte là ký tự Latin-1 riêng biệt, mỗi chuỗi nhiều byte UTF-8 sẽ tạo ra các ký tự thay thế sai. Nếu Café xuất hiện dưới dạng Caf theo sau là ký tự thay thế hoặc dưới dạng Caf? hoặc Caf cộng với U+FFFD, bạn đang gặp lỗi khác: bộ giải mã không nhận ra chuỗi byte là hợp lệ UTF-8. Một ví dụ cụ thể: Base64 SGVsbG8sIOS4lueVjCEg8J-Zgg== giải mã như sau.

TextDecoding và quyết định về bộ ký tự — giải mã thành UTF-8 và tại sao bộ ký tự lại là một thông tin thực tế riêng biệt mà bạn phải biết

atob tạo ra chuỗi nhị phân có byte (72, 101, 108, 108, 111, 44, 32, 228, 184, 180, 149, 140, 33, 32, 240, 159, 152, 130). Sáu byte đầu tiên là ASCII: trở thành Hello,. Byte 228, 184, 180 là chuỗi UTF-8 ba byte đại diện cho ký tự CJK. Byte 149, 140 là một phần của chuỗi tiếp theo. Chuỗi hoàn chỉnh bao gồm chuỗi biểu tượng cảm xúc bốn byte (240, 159, 152, 130) cho ký tự cuối cùng.

Khi được xử lý chính xác thông qua TextDecoding, tất cả các byte sẽ kết hợp để tạo ra văn bản có chữ viết hỗn hợp gốc. UTF-8 chuỗi byte có độ dài có thể dự đoán được: byte bắt đầu bằng 0xxxxxxx là một byte ASCII; byte bắt đầu bằng 110xxxxx dự kiến ​​byte tiếp theo bắt đầu bằng 10xxxxx (tổng cộng hai byte); byte bắt đầu bằng 1110xxxx dự kiến ​​sẽ có hai byte tiếp theo (tổng cộng ba byte); byte bắt đầu bằng 11110xxx dự kiến ​​sẽ có ba byte tiếp theo (tổng cộng bốn byte). Đối với mẫu CJK và biểu tượng cảm xúc hỗn hợp, chế độ xem byte đặc biệt mang tính chẩn đoán vì trực giác ASCII không còn hữu ích nữa. Một số byte thuộc về mỗi ký hiệu hiển thị và việc xóa một byte sẽ chuyển chuỗi còn lại thành UTF-8 không hợp lệ. Việc giải mã nghiêm ngặt biến sự thay đổi đó thành một lỗi được đặt tên thay vì thiệt hại có vẻ hợp lý.

Ví dụ đã hoạt động: giải mã chuỗi Base64 chứa CJK và biểu tượng cảm xúc - byte, điểm mã và chuỗi cuối cùng so với chuỗi gốc

Trình tự bắt đầu bằng 1111110x không hợp lệ trong UTF-8 (dành riêng cho tương lai, không được sử dụng). Byte bắt đầu bằng 10xxxxxx sẽ không bao giờ xuất hiện dưới dạng byte dẫn; nó là sự tiếp tục. Nếu luồng byte vi phạm quy tắc thì luồng byte đó không hợp lệ UTF-8. TextDecoding với bộ ký tự utf-8 diễn giải mảng theo các quy tắc này và thành công đối với các chuỗi hợp lệ. Không hợp lệ thì nó báo lỗi.

Bộ mã hóa và giải mã Base64 sử dụng TextDecoding với cờ chế độ nghiêm ngặt đúng. Điều này có nghĩa là UTF-8 không hợp lệ sẽ gây ra lỗi thay vì chèn các ký tự thay thế một cách âm thầm (U+FFFD). Nếu chuỗi Base64 giải mã thành byte không hợp lệ UTF-8, chế độ nghiêm ngặt sẽ loại bỏ thay vì tiếp tục với văn bản bị cắt xén. Đây là lựa chọn thiết kế: tải trọng nhị phân (hình ảnh, khóa, dữ liệu nén) không phải là văn bản và không được giải mã dưới dạng văn bản.

Nhận dạng mẫu: Ã, †và � - cách nhận biết vấn đề Base64 từ vấn đề về bộ ký tự

Nếu cố gắng giải mã JPEG dưới dạng Base64, luồng byte sẽ không thể hiện UTF-8 hợp lệ và việc giải mã nghiêm ngặt sẽ từ chối nó. Công cụ cung cấp chế độ xem hex cho các tải trọng như vậy: bạn có thể xem byte thô mà không cần giả vờ chúng là văn bản. Việc nhận biết lỗi giải mã UTF-8 liên quan đến việc xem xét byte theo ngữ cảnh. Chúng có phải là số lẻ trong đó chuỗi nhiều byte được mong đợi không?

Byte đầu tiên của chuỗi tiềm năng có hợp lệ không (bắt đầu bằng 10xxxxxx)? Các byte tiếp tục có bị thiếu không? Nút xuất byte cung cấp sự phân nhánh rõ ràng nhất trong quá trình điều tra. Nếu hex xuất hiện nhưng việc giải mã văn bản không thành công thì quá trình phân tích cú pháp Base64 đã thành công và tải trọng ở dạng nhị phân, bị hỏng hoặc được mã hóa bằng một bộ ký tự khác. Việc thay đổi dấu câu Base64 không thể sửa được bộ ký tự không khớp sau khi đã xuất hiện các byte chính xác.

Điều này không bao gồm những gì — UTF-16 tải trọng, đầu ra nhị phân như hình ảnh và xử lý byte không hợp lệ

Các mẫu đều nhất quán. Một byte đơn 0xFF không bao giờ hợp lệ trong UTF-8; nó không được là ASCII byte (chỉ 0–127 là ASCII) và không được là byte dẫn đầu (byte dẫn đầu là 0xC0–0xFD, 0xFF được đặt trước). Người đại diện duy nhất (khái niệm UTF-16) không thể xuất hiện trong UTF-8; nếu thấy chuỗi byte 0xED 0xA0 0x80 (mã hóa thay thế U+D800 theo kiểu UTF-8), thì chuỗi đó không hợp lệ UTF-8.

Một cách giải quyết lịch sử là btoa(unescape(encodeURIComponent(text))). EncodeURIComponent biến quán cà phê thành %C3%A9 (mã hóa phần trăm UTF-8 byte), unescape đóng gói lại dưới dạng đơn vị mã, btoa mã hóa đơn vị mã. Điều này hoạt động với hầu hết văn bản nhưng dễ vỡ xung quanh các đại diện đơn độc và khó đọc. Quy trình hiện đại—TextEncode thành byte, sau đó là base64—rõ ràng và tiêu chuẩn hơn. TextEncode được tích hợp vào tất cả các trình duyệt hiện đại và Node.js, giúp bạn đưa ra lựa chọn đúng đắn. UTF-16, mã hóa một byte cũ và nội dung tệp tùy ý yêu cầu bộ giải mã được chọn cho các byte đó hoặc trình xem nhận biết nhị phân. ToolAcre cố tình không đoán trong số đó. Việc đoán có thể biến một chuỗi không hợp lệ thành văn bản gây hiểu lầm, trong khi kết xuất hex sẽ bảo toàn từng byte để có cách diễn giải sáng suốt sau này.

Bài học rút ra: Base64 cung cấp cho bạn byte, UTF-8 cung cấp cho bạn văn bản - cách bộ mã hóa & giải mã Base64 thực hiện cả hai bước để văn bản được giải mã khớp chính xác với đầu vào

Khi bạn có chuỗi Base64 và muốn văn bản UTF-8, các bước hoàn chỉnh là: giải mã Base64 thành byte (sử dụng atob hoặc thư viện giải mã base64), tạo Uint8Array từ byte, chuyển mảng tới TextDecoding bằng bộ ký tự utf-8, đọc kết quả dưới dạng chuỗi.

Nếu đầu vào là dữ liệu nhị phân chứ không phải văn bản, hãy bỏ qua TextDecode và kiểm tra trực tiếp các byte. Bộ mã hóa và giải mã Base64 cung cấp chế độ xem byte thập lục phân, bảo toàn các giá trị mà quá trình giải mã UTF-8 nghiêm ngặt sẽ từ chối. Sự phân nhánh đó mang tính chẩn đoán: các byte thành công cộng với văn bản không thành công có nghĩa là phân tích cú pháp Base64 đã hoạt động, trong khi tải trọng là nhị phân, bị hỏng hoặc được mã hóa bằng bộ ký tự mà công cụ này không đoán được.