1. Giới thiệu
Giả sử bạn là một nhà ngoại giao ở hội nghị quốc tế: ai cũng nói ngôn ngữ riêng và dùng chữ viết riêng. Để hiểu nhau cần một bộ quy tắc chung — tập hợp quy ước để khớp ký tự. Trong máy tính, vai trò đó do các encoding đảm nhận.
Máy tính chỉ hiểu một "ngôn ngữ" — chuỗi các số 0 và 1. 0 và 1 là "bảng chữ cái" của nó. Mọi thông tin được lưu và truyền dưới dạng byte. Một byte là 8 bit (ví dụ 01000001).
Vậy làm sao liên kết chữ viết của chúng ta với byte? Làm sao máy tính biết rằng ký tự "A" không chỉ là một dãy 0/1 mà là chính ký tự hiển thị trên màn hình?
Encoding
Encoding là một tập quy tắc (bảng ánh xạ) xác định cách mỗi ký tự (chữ cái, chữ số, dấu, chữ Hán, emoji...) được chuyển thành chuỗi byte và cách các byte đó được diễn giải ngược lại thành ký tự.
Tương tự như mã Morse: bạn dịch văn bản thành dấu chấm và gạch ngang, gửi đi, người nhận dùng cùng quy tắc để phục hồi. Trong máy tính, thỏa thuận "byte ↔ ký tự" chính là encoding.
2. Tại sao chúng ta phải bận tâm về encoding?
- Chuyển giữa thế giới con người và máy: nếu không có encoding, text chỉ là dãy byte; có encoding thì mới có ký tự có ý nghĩa.
- Tính phổ quát và tương thích: các chương trình và hệ điều hành khác nhau phải "thỏa thuận" với nhau. Nếu file khai báo là UTF-8, thì đọc nó cũng phải theo UTF-8.
- Hỗ trợ nhiều ngôn ngữ và ký tự: Cyrillic, chữ Ả Rập, ký tự Hán, ký hiệu toán học, emoji — càng nhiều ký tự thì encoding càng phải phức tạp và linh hoạt.
3. ASCII – mã nguyên thủy
Một trong những encoding cổ nhất và cơ bản nhất là ASCII (American Standard Code for Information Interchange). Nó dùng 7 bit cho mỗi ký tự, tức có thể biểu diễn 128 ký tự khác nhau: bảng Latin (A-Z, a-z), chữ số (0-9), dấu câu và mã điều khiển (ví dụ xuống dòng, tab).
| Ký tự | Mã thập phân (ASCII) | Mã nhị phân (7 bit) |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Dấu cách | |
|
Về lịch sử, bit thứ tám thường được dùng làm parity bit, rồi sau đó bắt đầu được dùng trong các "mở rộng" của ASCII — từ đó xuất hiện nhiều bộ ký tự một byte theo locale khác nhau, gây ra hỗn loạn.
Nếu bạn ghi "Hello", trên đĩa về cơ bản sẽ là như sau (1 byte cho mỗi ký tự; với 7-bit ASCII bit cao = 0):
H (01001000) e (01100101) l (01101100) l (01101100) o (01101111)
Đơn giản. Nhưng chữ tiếng Nga hay chữ Hán đâu? Trong ASCII không có — nó là "từ điển một ngôn ngữ", chỉ phù hợp cho bộ Latin cơ bản.
4. Ký tự rác — tại sao encoding không phải chuyện đùa
Thỉnh thoảng khi mở file bạn thấy thứ kiểu Привет thay vì "Privet". Người ta gọi đó là "ký tự rác" (hoặc Mojibake) — kết quả của việc đọc byte theo encoding sai.
Giả sử bạn lưu "Privet, mir!" ở encoding Windows-1251, nơi byte cho các chữ của từ "Privet" có thể là như sau (đơn giản hoá):
- P → 207
- r → 240
- i → 232
- v → 226
- e → 229
- t → 242
Rồi đồng nghiệp mở file trong editor mong đợi ISO-8859-1 (Latin-1), hoặc bạn dùng StreamReader mà không chỉ rõ encoding, và encoding không khớp với file. Kết quả: byte 207 được diễn giải theo bảng khác — và text "vỡ".
| Ký tự gốc (Windows-1251) | Biểu diễn byte (ví dụ) | Ký tự đọc được khi coi là ISO-8859-1 |
|---|---|---|
| P | |
Ç |
| r | |
à |
| i | |
è |
| v | |
â |
| e | |
å |
| t | |
ò |
Kết quả là ta thấy Çàèâåò thay vì "Privet". Nếu ký tự hoàn toàn không có trong bảng encoding mong đợi, bạn sẽ thấy hình vuông hoặc dấu hỏi.
Bài học thực tế: khi đọc/ghi text, hãy luôn chỉ rõ encoding, nhất là khi nguồn dữ liệu không do bạn kiểm soát. Trong .NET làm điều này bằng cách dùng StreamReader/StreamWriter với Encoding phù hợp (ví dụ UTF-8 hoặc Encoding.GetEncoding("windows-1251")). Điều này giúp tránh "ký tự rác" và đảm bảo trao đổi dữ liệu chính xác giữa hệ thống.
Trong các bài sau mình sẽ chỉ cho cách chọn và chỉ định encoding khi làm việc với file và stream, để code của bạn đa ngôn ngữ, dễ port và ổn định.
GO TO FULL VERSION