Tổng quan về mã hóa ký tự
Máy tính chỉ hiểu các con số, do đó mọi văn bản đều phải được chuyển đổi thành dãy số trước khi xử lý. Đơn vị cơ bản nhất là bit, và 8 bit tạo thành một byte. Một byte có thể biểu diễn số nguyên tối đa là 255.
Trong giai đoạn đầu, hệ thống ASCII được tạo ra với 127 ký tự cơ bản (chữ cái Latinh, số và ký hiệu đặc biệt). Ví dụ, ký tự A có giá trị 65. Tuy nhiên, ASCII không đủ để biểu diễn các ngôn ngữ có bộ ký tự lớn như tiếng Trung, tiếng Nhật hay tiếng Việt.
Sự ra đời của các chuẩn riêng lẻ như GB2312, Shift_JIS dẫn đến tình trạng xung đột và lỗi hiển thị (loạn mã) khi trao đổi dữ liệu đa ngôn ngữ. Để giải quyết vấn đề này, Unicode được phát triển nhằm thống nhất tất cả các ký tự của mọi ngôn ngữ trên thế giới vào một bảng mã duy nhất.
Mặc dù Unicode giải quyết được vấn đề loạn mã, nhưng việc sử dụng cố định 2 hoặc 4 byte cho mỗi ký tự gây lãng phí dung lượng, đặc biệt với văn bản chủ yếu là tiếng Anh. Do đó, UTF-8 xuất hiện như một giải pháp mã hóa độ dài thay thế. UTF-8 sử dụng 1 byte cho ký tự ASCII, 3 byte cho các ký tự phổ biến như tiếng Việt hay tiếng Trung, và tối đa 4 byte cho các ký tự hiếm.
| Ký tự | ASCII | Unicode | UTF-8 |
|---|---|---|---|
| Z | 01011010 | 00000000 01011010 | 01011010 |
| Việt | Không hỗ trợ | 00100001 00000101 | 11100010 10000100 10000101 |
Trong thực tế phát triển phần mềm, bộ nhớ trong (RAM) thường lưu trữ chuỗi dưới dạng Unicode, trong khi dữ liệu lưu trữ trên ổ cứng hoặc truyền qua mạng sẽ được chuyển đổi sang UTF-8 để tối ưu hóa dung lượng.
Xử lý chuỗi trong Python
Từ phiên bản Python 3, mọi chuỗi văn bản (str) đều được lưu trữ dưới dạng Unicode, hỗ trợ đa ngôn ngữ mặc định.
# In ra chuỗi chứa ký tự đặc biệt
message = "Lập trình Python rất thú vị!"
print(message)
Để lấy mã số nguyên của một ký tự, ta dùng hàm ord(), và ngược lại, dùng chr() để chuyển mã số thành ký tự:
# Chuyển đổi giữa ký tự và mã số
char_code = ord('Z') # Trả về 90
print(char_code)
decoded_char = chr(89) # Trả về 'Y'
print(decoded_char)
Ngoài ra, bạn có thể biểu diễn ký tự bằng mã hexa trong chuỗi:
# Sử dụng mã Unicode hexa
text = '\u0050\u0079\u0074\u0068\u006f\u006e'
print(text) # Output: Python
Khi truyền tải dữ liệu qua mạng hoặc ghi vào file, chuỗi str cần được chuyển đổi thành kiểu bytes. Kiểu bytes được ký hiệu bằng tiền tố b.
# Phân biệt str và bytes
string_data = "ABC"
bytes_data = b"XYZ"
# Mã hóa str sang bytes
encoded_ascii = string_data.encode('ascii')
encoded_utf8 = "Mã hóa".encode('utf-8')
print(encoded_ascii) # b'ABC'
print(encoded_utf8) # b'M\xc3\xa3 h\xc3\xb3a'
Nếu cố gắng mã hóa ký tự không thuộc bảng ASCII bằng chuẩn ASCII, Python sẽ ném ra lỗi UnicodeEncodeError.
Ngược lại, để giải mã bytes thành str, ta sử dụng phương thức decode():
# Giải mã bytes sang str
raw_bytes = b"Network"
decoded_text = raw_bytes.decode('ascii')
utf8_bytes = b'M\xc3\xa3 h\xc3\xb3a'
decoded_utf8 = utf8_bytes.decode('utf-8')
print(decoded_text) # Network
print(decoded_utf8) # Mã hóa
Nếu luồng byte chứa dữ liệu không hợp lệ, bạn có thể bỏ qua chúng bằng tham số errors='ignore':
invalid_bytes = b'M\xc3\xa3\xff'
safe_decode = invalid_bytes.decode('utf-8', errors='ignore')
print(safe_decode) # Mã
Hàm len() hoạt động khác nhau tùy thuộc vào kiểu dữ liệu. Với str, nó đếm số lượng ký tự. Với bytes, nó đếm số lượng byte:
word_str = "Code"
word_bytes = word_str.encode('utf-8')
print(len(word_str)) # 4 (ký tự)
print(len(word_bytes)) # 4 (byte)
vn_str = "Việt"
vn_bytes = vn_str.encode('utf-8')
print(len(vn_str)) # 4 (ký tự)
print(len(vn_bytes)) # 6 (byte, do ký tự 'ệ' tốn 3 byte)
Khi làm việc với file mã nguồn Python chứa ký tự đa ngôn ngữ, hãy đảm bảo trình soạn thảo lưu file dưới dạng UTF-8 và thêm khai báo sau vào đầu file:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
Định dạng chuỗi
Python hỗ trợ nhiều cách để chèn biến vào chuỗi. Phương pháp truyền thống sử dụng toán tử %, tương tự như ngôn ngữ C.
# Định dạng cơ bản với %
name = "Alice"
age = 28
greeting = "Xin chào, %s. Bạn %d tuổi." % (name, age)
print(greeting)
Các trình giữ chỗ (placeholder) phổ biến bao gồm:
| Trình giữ chỗ | Ý nghĩa |
|---|---|
%d | Số nguyên |
%f | Số thực |
%s | Chuỗi (có thể dùng cho mọi kiểu dữ liệu) |
%x | Số nguyên hệ 16 |
Để in ra ký tự % trong chuỗi, hãy sử dụng %%:
discount = 20
print("Giảm giá: %d %%" % discount) # Giảm giá: 20 %
Một cách tiếp cận hiện đại và linh hoạt hơn là sử dụng phương thức format():
# Sử dụng format()
product = "Laptop"
price = 1250.75
output = "Sản phẩm: {0}, Giá: {1:.2f} USD".format(product, price)
print(output)
Bài tập thực hành
Nhiệt độ trung bình của một thành phố tăng từ 24.5 độ C lên 26.2 độ C. Hãy tính phần trăm mức tăng và in ra màn hình với định dạng 'x.x%' (làm tròn 1 chữ số thập phân).
# -*- coding: utf-8 -*-
old_temp = 24.5
new_temp = 26.2
# Tính phần trăm tăng trưởng
increase_percent = ((new_temp - old_temp) / old_temp) * 100
# Định dạng và in kết quả
print("Mức tăng nhiệt độ: %.1f%%" % increase_percent)