1. Mã hóa mặc định
Khi bạn làm việc với StreamReader hoặc StreamWriter, chúng sử dụng mã hóa mặc định.
Nếu bạn viết:
using var reader = new StreamReader("myfile.txt");
hoặc
using var writer = new StreamWriter("output.txt");
.NET sẽ chọn mã hóa mặc định. Trên Windows thường là UTF-8 (thường không có BOM), nhưng trong các ứng dụng cũ hoặc khi chạy trên hệ thống khác hành vi có thể khác.
Trong hầu hết trường hợp UTF-8 là lựa chọn tốt: nó hỗ trợ tất cả ngôn ngữ và lưu trữ gọn văn bản tiếng Anh (và tiếng Nga cũng khá ổn). Nhưng nếu file được tạo bằng mã hóa khác (ví dụ Windows-1251), hoặc có chương trình khác sẽ đọc file và mong đợi mã hóa cụ thể, bạn cần chỉ định rõ mã hóa.
2. Chỉ định mã hóa cho StreamReader và StreamWriter
Các biến thể constructor
Cả StreamReader và StreamWriter đều hỗ trợ constructor mà bạn có thể truyền một đối tượng kiểu System.Text.Encoding.
Ví dụ:
using var reader = new StreamReader("myfile.txt", Encoding.UTF8);
using var writer = new StreamWriter("output.txt", false, Encoding.UTF8);
Nếu bạn muốn làm việc với ASCII, Windows-1251 hoặc UTF-16, chỉ cần thay mã hóa khác:
using var reader = new StreamReader("myfile.txt", Encoding.Unicode); // UTF-16
using var reader2 = new StreamReader("rus.txt", Encoding.GetEncoding("windows-1251"));
Bảng ngắn các mã hóa phổ biến:
| Mã hóa | Biểu thức C# | Mô tả |
|---|---|---|
| UTF-8 | |
Hỗ trợ mọi ngôn ngữ, compact |
| UTF-16 (Unicode) | |
Chuẩn .NET, 2 byte cho mỗi ký tự |
| ASCII | |
Chỉ ký tự cơ bản (tiếng Anh) |
| Windows-1251 | |
Tiếng Nga, các chương trình Windows cũ |
Lưu ý! Với các mã hóa không tiêu chuẩn như Windows-1251 hãy dùng phương thức Encoding.GetEncoding("windows-1251").
3. Đọc và ghi với mã hóa chỉ định
Ta thêm tính năng mới: chương trình đọc file với mã hóa được chỉ định và ghi kết quả ra file mới, cũng với mã hóa rõ ràng. Như vậy bạn có thể chuyển đổi văn bản giữa các hệ thống!
Ví dụ 1: Đọc file mã hóa Windows-1251 và in ra màn hình
// Giả sử tệp được tạo bằng Windows-1251 cũ
using var reader = new StreamReader("ru_text.txt", Encoding.GetEncoding("windows-1251"));
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
Tại sao quan trọng: Nếu không chỉ định mã hóa, thay vì chữ tiếng Nga bạn sẽ thấy ký tự rác như "Uchebnik".
Ví dụ 2: Ghi file ở UTF-16 (Unicode)
using var writer = new StreamWriter("utf16text.txt", false, Encoding.Unicode);
writer.WriteLine("Xin chào, thế giới!");
writer.WriteLine("Hello, world!");
writer.WriteLine("こんにちは世界");
Kết quả: Mở file bằng Notepad chẳng hạn, bạn sẽ thấy các ký tự hiển thị bình thường.
4. Một tiện ích nhỏ để chuyển mã hóa
Bài toán (thường gặp trong phỏng vấn!): Chuyển một file text từ mã hóa Windows-1251 sang UTF-8.
string sourcePath = "ru_text_1251.txt";
string destPath = "ru_text_utf8.txt";
// Đọc bằng mã hóa Windows-1251
using var reader = new StreamReader(sourcePath, Encoding.GetEncoding("windows-1251"));
// Ghi bằng UTF-8
using var writer = new StreamWriter(destPath, false, Encoding.UTF8);
string? line;
while ((line = reader.ReadLine()) != null)
writer.WriteLine(line);
Bây giờ ru_text_utf8.txt có thể mở ngon lành trong VSCode, Linux, Mac và hiển thị văn bản dễ đọc.
5. Những chi tiết hữu ích
Làm sao biết file đang dùng mã hóa gì?
Câu hỏi có bẫy! File thường không lưu thông tin về mã hóa bên trong (ngoại trừ file có BOM, sẽ nói kỹ hơn ở bài sau). Nếu mở file với mã hóa sai, kết quả là rác.
Các cách tiếp cận thực tế:
- Nếu file do chương trình của bạn tạo — luôn dùng cùng mã hóa khi đọc như lúc ghi.
- Nếu file đến từ nguồn không rõ — thử các mã hóa khác nhau hoặc dùng công cụ bên ngoài để nhận diện.
Làm việc với emoji và ký tự lạ
UTF-8 và UTF-16 cho phép thoải mái dùng emoji và ký tự từ nhiều ngôn ngữ. Thử lưu file có emoji bằng UTF-8, rồi mở bằng mã hóa cũ — sẽ vui (hoặc không)!
Mã ví dụ với emoji:
using var writer = new StreamWriter("emoji.txt", false, Encoding.UTF8);
writer.WriteLine("Xin chào 👋😀🌍");
StreamReader với tự động nhận diện mã hóa
Constructor của StreamReader có overload với tham số detectEncodingFromByteOrderMarks:
new StreamReader(path, encoding, detectEncodingFromByteOrderMarks: true)
Nếu tham số này là true (đây là giá trị mặc định!), StreamReader sẽ cố đoán mã hóa dựa trên BOM (một "dấu" ở đầu file). Nhưng nếu file không có BOM hoặc dùng mã hóa lạ, cách đoán này không hiệu quả.
Khi nào và vì sao nên chỉ định mã hóa rõ ràng?
- Dữ liệu đa ngôn ngữ: hỗ trợ tiếng Ả Rập, Trung, emoji, v.v.
- Tích hợp: file được đọc/ghi không chỉ bởi chương trình của bạn mà còn bởi hệ thống khác, họ mong mã hóa cụ thể.
- Kích thước file quan trọng: ASCII/UTF-8 tiết kiệm cho chữ Latin, UTF-16 tốt cho ký tự tượng hình.
- Tương thích: logs/export cho ứng dụng khác.
6. Lỗi thường gặp khi làm việc với mã hóa
Lỗi #1: không chỉ định mã hóa khi đọc file.
Đọc file có tiếng Nga (hoặc văn bản không phải Latin) mà không chỉ định mã hóa thường dẫn đến "abrákadabra" không đọc được.
Lỗi #2: không khớp mã hóa giữa ghi và đọc.
File được ghi bằng mã hóa này nhưng mở bằng mã hóa khác — kết quả là text bị méo. Thường xảy ra khi di chuyển giữa OS hoặc ứng dụng khác nhau.
Lỗi #3: mã hóa không được hỗ trợ hoặc hỗ trợ không đầy đủ.
Một số editor không xử lý tốt UTF-8 không có BOM hoặc các mã hóa 1-byte cũ như Windows-1251. Đôi khi phải thủ công đổi mã hóa và thử nghiệm.
GO TO FULL VERSION