Bước tới nội dung

Hướng dẫn sử dụng ký tự đặc biệt Unicode/Chương 2: Các bảng mã ký tự

Tủ sách mở Wikibooks

Chương 2: Các bảng mã ký tự

[sửa]

Chương này giới thiệu các phương thức mã hóa (encoding) được sử dụng để lưu trữ và truyền tải ký tự Unicode. Hiểu rõ các bảng mã giúp bạn xử lý văn bản chính xác và tránh lỗi hiển thị.

2.1 ASCII - Nền tảng của mọi bảng mã

[sửa]

2.1.1 Giới thiệu ASCII

[sửa]

ASCII (American Standard Code for Information Interchange) là bảng mã ký tự đầu tiên được chuẩn hóa, ra đời năm 1963.

Đặc điểm:

  • Sử dụng 7 bit để biểu diễn mỗi ký tự
  • Tổng cộng 128 ký tự (27 = 128)
  • Tương thích với mọi hệ thống máy tính hiện đại

2.1.2 Cấu trúc bảng ASCII

[sửa]
Phạm vi Loại ký tự Ví dụ
0–31 Ký tự điều khiển Tab (9), Enter (13), Escape (27)
32–47 Dấu câu và ký hiệu Space (32), ! (33), " (34)
48–57 Chữ số 0–9
65–90 Chữ cái in hoa A–Z
97–122 Chữ cái thường a–z
123–127 Ký hiệu bổ sung (124), } (125)

Bảng ASCII đầy đủ (ký tự in được):

Dec  Hex  Ký tự    Dec  Hex  Ký tự    Dec  Hex  Ký tự
 32  20   (space)   64  40   @        96  60   `
 33  21   !         65  41   A        97  61   a
 34  22   "         66  42   B        98  62   b
 35  23   #         67  43   C        99  63   c
 36  24   $         68  44   D       100  64   d
 37  25   %         69  45   E       101  65   e
 38  26   &         70  46   F       102  66   f
 39  27   '         71  47   G       103  67   g
 40  28   (         72  48   H       104  68   h
 41  29   )         73  49   I       105  69   i
 42  2A   *         74  4A   J       106  6A   j
 43  2B   +         75  4B   K       107  6B   k
 44  2C   ,         76  4C   L       108  6C   l
 45  2D   -         77  4D   M       109  6D   m
 46  2E   .         78  4E   N       110  6E   n
 47  2F   /         79  4F   O       111  6F   o
 48  30   0         80  50   P       112  70   p
 49  31   1         81  51   Q       113  71   q
 50  32   2         82  52   R       114  72   r
 51  33   3         83  53   S       115  73   s
 52  34   4         84  54   T       116  74   t
 53  35   5         85  55   U       117  75   u
 54  36   6         86  56   V       118  76   v
 55  37   7         87  57   W       119  77   w
 56  38   8         88  58   X       120  78   x
 57  39   9         89  59   Y       121  79   y
 58  3A   :         90  5A   Z       122  7A   z
 59  3B   ;         91  5B   [       123  7B   {
 60  3C   <         92  5C   \       124  7C   |
 61  3D   =         93  5D   ]       125  7D   }
 62  3E   >         94  5E   ^       126  7E   ~
 63  3F   ?         95  5F   _

2.1.3 Hạn chế của ASCII

[sửa]

ASCII chỉ đủ cho tiếng Anh cơ bản, không hỗ trợ:

  • Chữ cái có dấu (é, ñ, ü)
  • Chữ viết không phải Latinh (Cyrillic, Hy Lạp, Ả Rập)
  • Chữ tượng hình (Hán, Nhật, Hàn)
  • Tiếng Việt với các dấu thanh

2.2 Các bảng mã mở rộng

[sửa]

2.2.1 Extended ASCII (8-bit)

[sửa]

Nhiều bảng mã mở rộng ASCII lên 8 bit (256 ký tự), giữ nguyên 128 ký tự đầu của ASCII và thêm 128 ký tự mới:

  • ISO 8859-1 (Latin-1): Tây Âu (Pháp, Đức, Tây Ban Nha)
  • ISO 8859-2 (Latin-2): Trung Âu (Ba Lan, Séc, Hungary)
  • ISO 8859-5: Cyrillic (Nga)
  • ISO 8859-6: Ả Rập
  • Windows-1252: Phiên bản Microsoft của Latin-1

2.2.2 Các bảng mã tiếng Việt cũ

[sửa]

Trước khi Unicode phổ biến, tiếng Việt sử dụng nhiều bảng mã không tương thích:

Bảng mã Đặc điểm Sử dụng
TCVN3 (ABC) 2 byte cho mỗi ký tự có dấu Cơ quan nhà nước
VNI Dùng ký tự đặc biệt cho dấu Windows cũ
VISCII Mã hóa 8-bit Unix, Internet đầu
VPS Tương tự VISCII Macintosh

💡 Lưu ý: Ngày nay, tất cả văn bản tiếng Việt nên sử dụng Unicode (UTF-8). Các bảng mã cũ chỉ còn gặp trong tài liệu lưu trữ.

2.3 UTF-8 - Tiêu chuẩn phổ biến nhất

[sửa]

2.3.1 Giới thiệu UTF-8

[sửa]

UTF-8 (Unicode Transformation Format - 8 bit) là phương thức mã hóa Unicode phổ biến nhất hiện nay, được phát minh bởi Ken Thompson và Rob Pike năm 1992.

Đặc điểm nổi bật:

  • Mã hóa độ dài thay đổi (1-4 byte)
  • Tương thích ngược với ASCII
  • Tự đồng bộ (self-synchronizing)
  • Không có vấn đề thứ tự byte (endianness)

2.3.2 Cách mã hóa UTF-8

[sửa]

UTF-8 sử dụng 1 đến 4 byte tùy thuộc vào code point:

Phạm vi code point Số byte Mẫu bit Ví dụ
U+0000 – U+007F 1 0xxxxxxx A (U+0041) → 41
U+0080 – U+07FF 2 110xxxxx 10xxxxxx é (U+00E9) → C3 A9
U+0800 – U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx 中 (U+4E2D) → E4 B8 AD
U+10000 – U+10FFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 😀 (U+1F600) → F0 9F 98 80

Ví dụ chi tiết - Mã hóa chữ "ă" (U+0103):

  1. Code point: U+0103 = 0000 0001 0000 0011 (nhị phân)
  2. Thuộc phạm vi U+0080 – U+07FF → dùng 2 byte
  3. Mẫu: 110xxxxx 10xxxxxx
  4. Điền bit: 11000100 10000011 = C4 83

2.3.3 Ưu điểm của UTF-8

[sửa]

Tương thích ASCII:

  • Văn bản ASCII thuần túy là UTF-8 hợp lệ
  • Phần mềm cũ vẫn có thể xử lý phần ASCII

Hiệu quả lưu trữ:

  • Tiếng Anh: 1 byte/ký tự (như ASCII)
  • Tiếng Việt: 2-3 byte/ký tự
  • Tiếng Trung/Nhật: 3 byte/ký tự
  • Emoji: 4 byte/ký tự

Phát hiện lỗi:

  • Cấu trúc byte rõ ràng giúp phát hiện dữ liệu hỏng
  • Có thể khôi phục từ giữa chuỗi byte

2.3.4 UTF-8 trong thực tế

[sửa]

Theo thống kê W3Techs (2024), UTF-8 chiếm hơn 98% các trang web. Nó là mã hóa mặc định cho:

  • HTML5
  • JSON
  • XML (khuyến nghị)
  • Nhiều ngôn ngữ lập trình (Python 3, Rust, Go)
  • Git và các hệ thống quản lý mã nguồn

2.4 UTF-16 - Mã hóa 16-bit

[sửa]

2.4.1 Giới thiệu UTF-16

[sửa]

UTF-16 sử dụng đơn vị 16-bit (2 byte) làm đơn vị cơ bản. Ký tự trong BMP dùng 1 đơn vị (2 byte), ký tự ngoài BMP dùng 2 đơn vị (4 byte - surrogate pair).

2.4.2 Surrogate Pairs

[sửa]

Ký tự có code point > U+FFFF được mã hóa bằng cặp surrogate:

  • High surrogate: U+D800 – U+DBFF
  • Low surrogate: U+DC00 – U+DFFF

Công thức chuyển đổi:

Cho code point CP (với CP > 0xFFFF):

  1. CP' = CP - 0x10000
  2. High surrogate = 0xD800 + (CP' >> 10)
  3. Low surrogate = 0xDC00 + (CP' & 0x3FF)

Ví dụ - Emoji 😀 (U+1F600):

  1. CP' = 0x1F600 - 0x10000 = 0xF600
  2. High = 0xD800 + (0xF600 >> 10) = 0xD800 + 0x3D = 0xD83D
  3. Low = 0xDC00 + (0xF600 & 0x3FF) = 0xDC00 + 0x200 = 0xDE00
  4. Kết quả: D83D DE00

2.4.3 UTF-16 BE và LE

[sửa]

Do sử dụng đơn vị 16-bit, UTF-16 có vấn đề thứ tự byte:

  • UTF-16 BE (Big Endian): Byte cao trước
  • UTF-16 LE (Little Endian): Byte thấp trước

BOM (Byte Order Mark):

  • U+FEFF ở đầu file để chỉ định thứ tự byte
  • UTF-16 BE: FE FF
  • UTF-16 LE: FF FE

2.4.4 Sử dụng UTF-16

[sửa]

UTF-16 được dùng nội bộ trong:

  • Windows (API và hệ thống file NTFS)
  • Java (kiểu String)
  • JavaScript (kiểu String)
  • .NET Framework

2.5 UTF-32 - Mã hóa cố định

[sửa]

2.5.1 Đặc điểm UTF-32

[sửa]

UTF-32 sử dụng chính xác 4 byte cho mọi ký tự, cho phép ánh xạ trực tiếp 1-1 với code point.

Ưu điểm:

  • Truy cập ký tự theo chỉ số O(1)
  • Đơn giản để xử lý

Nhược điểm:

  • Tốn bộ nhớ (4 byte cho mọi ký tự)
  • Ít được sử dụng trong lưu trữ hoặc truyền tải

2.5.2 Sử dụng UTF-32

[sửa]

UTF-32 chủ yếu dùng cho xử lý nội bộ khi cần thao tác từng ký tự:

  • Python 3 (một số bản dựng)
  • Một số thư viện xử lý văn bản

2.6 So sánh các phương thức mã hóa

[sửa]
Tiêu chí UTF-8 UTF-16 UTF-32
Kích thước đơn vị 8 bit 16 bit 32 bit
Byte/ký tự 1-4 2-4 4
Tương thích ASCII Không Không
Vấn đề endianness Không
Hiệu quả cho tiếng Anh Cao Trung bình Thấp
Hiệu quả cho CJK Trung bình Cao Thấp
Phổ biến Web Rất cao Thấp Rất thấp

2.7 Chuyển đổi giữa các bảng mã

[sửa]

2.7.1 Nguyên tắc chuyển đổi

[sửa]

Khi chuyển đổi giữa các bảng mã:

  1. Giải mã (decode): Chuyển từ byte sang code point
  2. Mã hóa (encode): Chuyển từ code point sang byte

Lưu ý quan trọng:

  • Không phải mọi byte sequence đều hợp lệ trong mọi encoding
  • Chuyển đổi có thể mất dữ liệu nếu encoding đích không hỗ trợ ký tự

2.7.2 Ví dụ trong Python

[sửa]
# Mã hóa chuỗi Unicode sang UTF-8
text = "Xin chào Việt Nam! 🇻🇳"
utf8_bytes = text.encode('utf-8')
print(utf8_bytes)
# b'Xin ch\xc3\xa0o Vi\xe1\xbb\x87t Nam! \xf0\x9f\x87\xbb\xf0\x9f\x87\xb3'

# Giải mã UTF-8 sang Unicode
decoded = utf8_bytes.decode('utf-8')
print(decoded)
# Xin chào Việt Nam! 🇻🇳

# Chuyển đổi UTF-8 sang UTF-16
utf16_bytes = text.encode('utf-16')
print(utf16_bytes.hex())

2.7.3 Xử lý lỗi mã hóa

[sửa]

Khi gặp byte không hợp lệ, có các chiến lược xử lý:

  • strict: Báo lỗi (mặc định)
  • ignore: Bỏ qua ký tự lỗi
  • replace: Thay bằng ký tự thay thế (thường là ?)
  • backslashreplace: Thay bằng escape sequence
# Ví dụ xử lý lỗi
invalid_bytes = b'\x80\x81\x82'
print(invalid_bytes.decode('utf-8', errors='replace'))
# ���
print(invalid_bytes.decode('utf-8', errors='ignore'))
# (chuỗi rỗng)

2.8 Nhận dạng bảng mã

[sửa]

2.8.1 Dựa vào BOM

[sửa]
BOM (hex) Encoding
EF BB BF UTF-8
FE FF UTF-16 BE
FF FE UTF-16 LE
00 00 FE FF UTF-32 BE
FF FE 00 00 UTF-32 LE

2.8.2 Phát hiện tự động

[sửa]

Khi không có BOM, có thể dùng các thư viện phát hiện encoding:

  • Python: chardet, charset-normalizer
  • JavaScript: jschardet
  • Java: juniversalchardet

⚠️ Cảnh báo: Phát hiện tự động không phải lúc nào cũng chính xác 100%. Nên xác định encoding rõ ràng khi có thể.

Câu hỏi ôn tập

[sửa]
  1. ASCII có bao nhiêu ký tự? Tại sao ASCII không đủ cho tiếng Việt?
  2. UTF-8 mã hóa chữ "ê" (U+00EA) thành bao nhiêu byte? Tính cụ thể.
  3. Tại sao UTF-8 phổ biến hơn UTF-16 trên Web?
  4. Surrogate pair là gì? Khi nào cần sử dụng?
  5. BOM có tác dụng gì? UTF-8 có cần BOM không?

Bài tập thực hành

[sửa]
  1. Viết chương trình chuyển đổi một chuỗi tiếng Việt sang các dạng UTF-8, UTF-16 và in ra dãy byte.
  2. Tạo file văn bản với các encoding khác nhau và so sánh kích thước file.
  3. Viết hàm phát hiện encoding của một file dựa vào BOM.

Tham khảo

[sửa]