CodeGym /课程 /C# SELF /读写时指定编码 ( Encoding)

读写时指定编码 ( Encoding)

C# SELF
第 37 级, 课程 2
可用

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
Encoding.UTF8
支持所有语言,对拉丁字母很紧凑
UTF-16 (Unicode)
Encoding.Unicode
.NET 的标准,通常每个字符 2 字节
ASCII
Encoding.ASCII
只支持基本字符(英文)
Windows-1251
Encoding.GetEncoding("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)支持不佳。有时需要手动切换编码试试。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION