1. 默认编码
当你用 StreamReader 或 StreamWriter 时,它们会使用默认编码。
如果你写:
using var reader = new StreamReader("myfile.txt");
或者
using var writer = new StreamWriter("output.txt");
.NET 会选取一个默认编码。在 Windows 上通常是 UTF-8(常见的是没有 BOM),但在老程序里或跑在其他系统上时行为可能不同。
大多数情况下 UTF-8 是个不错的选择:它支持所有语言,对英文(以及俄文)也比较紧凑。但如果文件是用别的编码创建的(比如 Windows-1251),或者其他程序会以特定编码来读取它,你就需要显式指定正确的编码。
2. 为 StreamReader 和 StreamWriter 指定编码
构造函数选项
StreamReader 和 StreamWriter 都支持在构造函数里传入一个 System.Text.Encoding 对象。
示例:
using var reader = new StreamReader("myfile.txt", Encoding.UTF8);
using var writer = new StreamWriter("output.txt", false, Encoding.UTF8);
如果你想用 ASCII、Windows-1251 或 UTF-16,就换成别的 Encoding:
using var reader = new StreamReader("myfile.txt", Encoding.Unicode); // UTF-16
using var reader2 = new StreamReader("rus.txt", Encoding.GetEncoding("windows-1251"));
常用编码简表:
| 编码 | C# 表达式 | 说明 |
|---|---|---|
| UTF-8 | |
支持所有语言,对拉丁字母很紧凑 |
| UTF-16 (Unicode) | |
.NET 的标准,通常每个字符 2 字节 |
| ASCII | |
只支持基本字符(英文) |
| Windows-1251 | |
俄语,老 Windows 程序常用 |
注意! 对于像 Windows-1251 这样非标准的编码,使用 Encoding.GetEncoding("windows-1251")。
3. 用指定编码读写
我们给正在开发的小程序加个功能:按指定编码读文件,然后用指定编码写到新文件里。这样就能在不同系统间转换文本了!
示例 1:以 Windows-1251 编码读取文件并输出到控制台
// 假设文件是用老的 Windows-1251 创建的
using var reader = new StreamReader("ru_text.txt", Encoding.GetEncoding("windows-1251"));
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
为什么很重要:如果我们不指定编码,俄文字会变成难看的乱码,比如 "Учебник"。
示例 2:按 UTF-16 (Unicode) 写文件
using var writer = new StreamWriter("utf16text.txt", false, Encoding.Unicode);
writer.WriteLine("你好,世界!");
writer.WriteLine("Hello, world!");
writer.WriteLine("こんにちは世界");
结果:用 Notepad 打开时,所有字符都会正确显示。
4. 一个小工具:编码转换
面试里常见的一个任务:把文本文件从 Windows-1251 转成 UTF-8。
string sourcePath = "ru_text_1251.txt";
string destPath = "ru_text_utf8.txt";
// 用 Windows-1251 读取
using var reader = new StreamReader(sourcePath, Encoding.GetEncoding("windows-1251"));
// 用 UTF-8 写入
using var writer = new StreamWriter(destPath, false, Encoding.UTF8);
string? line;
while ((line = reader.ReadLine()) != null)
writer.WriteLine(line);
现在 ru_text_utf8.txt 可以安全地在 VSCode、Linux、Mac 上打开,看到可读文本。
5. 有用的小技巧
怎么判断文件是什么编码?
这是个有坑的问题!文件通常不会在内部保存它的编码信息(除了带有 BOM 的文件,关于 BOM 我们会在下一讲详细说)。如果你用错编码打开文件,看到的就是乱码。
常见实际做法:
- 如果文件是你程序生成的——读写始终用相同的编码。
- 如果文件来自未知来源——尝试几种编码,或者用第三方工具检测。
处理 emoji 和奇怪字符
UTF-8 和 UTF-16 可以很方便地支持 emoji 和世界各地的字符。试着把带 emoji 的文件保存为 UTF-8,然后用老编码去打开——会很“有趣”(或不那么有趣)。
带 emoji 的代码:
using var writer = new StreamWriter("emoji.txt", false, Encoding.UTF8);
writer.WriteLine("你好 👋😀🌍");
StreamReader 的自动编码检测
StreamReader 的构造函数有个带 detectEncodingFromByteOrderMarks 的重载:
new StreamReader(path, encoding, detectEncodingFromByteOrderMarks: true)
如果这个参数是 true(这是默认值!),StreamReader 会尝试根据文件开头的 BOM 来猜编码。但如果文件没有 BOM 或者是很冷门的编码,猜测就不靠谱。
什么时候必须显式指定编码?
- 多语言数据:需要支持阿拉伯语、中文、emoji 等时。
- 集成场景:文件不仅你自己的程序读写,还有别的系统按照特定编码来读取。
- 文件大小敏感:对拉丁字母来说 ASCII/UTF-8 更省空间,汉字/日文等用 UTF-16 有时更合适。
- 兼容性:导出日志或数据给其他应用时要保证编码一致。
6. 处理编码时常见错误
错误 1:读取文件时没显式指定编码。
不指定编码读取俄文或其他非拉丁文本,常常会看到不可读的“乱码”。
错误 2:写入和读取时编码不一致。
文件用一种编码写,但用另一种编码打开——结果就是文本被破坏。这个问题常发生在不同操作系统或不同程序之间传输文件时。
错误 3:不受支持或部分支持的编码。
有些编辑器对 UTF-8(无 BOM)或老的单字节编码(比如 Windows-1251)支持不佳。有时需要手动切换编码试试。
GO TO FULL VERSION