c#编码转换最安全通用方案是encoding.convert;仅utf-8↔utf-16时可用encoding.utf8.getbytes/getstring;禁用encoding.default因其跨平台不一致、不明确且不可靠,应显式指定如utf-8或gbk并配置异常回退。

直接说结论:C#里做编码转换,Encoding.Convert 是最安全、最通用的方案;但如果你只在 UTF-8 ↔ UTF-16(即 .NET 默认字符串)之间转,用 Encoding.UTF8.GetBytes + Encoding.UTF8.GetString 就够了,别绕弯。
为什么不能直接用 Encoding.Default 做中文字串转换?
Encoding.Default 在 Windows 上通常是 GB2312 或 GBK(取决于系统区域设置),不是固定值。同一段代码在简体中文系统能正常显示“你好”,换到英文 Windows 就可能变成乱码或抛 DecoderFallbackException。
- 它不跨平台——.NET Core/.NET 5+ 中
Encoding.Default已被标记为“仅限 Windows”,Linux/macOS 返回 UTF-8,行为不一致 - 它不明确——你无法从代码一眼看出当前用的是 GBK 还是 GB18030,后期维护成本高
- 它不可靠——读取外部文件时,若文件实际是 UTF-8 但用
Encoding.Default解码,中文直接变
替代做法:显式指定编码,比如 Encoding.GetEncoding("GBK") 或 Encoding.UTF8,并捕获 ArgumentException 处理不支持的名称。
UTF-8 ↔ GBK 转换必须用 Encoding.Convert
GBK 和 UTF-8 字节序列长度不等长、映射关系非一一对应,不能靠“先解码再编码”两步走(比如 GBK.GetString(bytes).ToCharArray() 再用 UTF-8 编回去),中间经过 .NET 的 UTF-16 字符串会丢失原始字节语义,尤其遇到 GBK 中的“双字节区外字符”或 UTF-8 中的代理对时,极易出错。
- 正确写法:
byte[] gbkBytes = Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding("GBK"), utf8Bytes); - 注意顺序:
Encoding.Convert(srcEncoding, dstEncoding, bytes),第一个是源编码,第二个是目标编码 - 如果目标编码不支持某字符(如 GBK 无法表示某些生僻 Unicode 字),默认会替换成问号
?;如需抛异常,得提前设置EncoderFallback和DecoderFallback
示例:从 UTF-8 字节数组转 GBK 并写入文件
byte[] utf8Data = Encoding.UTF8.GetBytes("测试中文");
byte[] gbkData = Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding("GBK"), utf8Data);
File.WriteAllBytes("out.txt", gbkData); // 此时文件是 GBK 编码
GetBytes / GetString 不等于“编码转换”,只是编/解码操作
GetBytes 是把字符串(内部永远是 UTF-16)按指定编码规则“压成”字节;GetString 是把字节按指定编码规则“还原”成字符串。它们本身不改变编码类型,只是桥梁。
- 误用场景:想把一个 GBK 编码的 byte[] 当作 UTF-8 解码 →
Encoding.UTF8.GetString(gbkBytes)→ 出现乱码或异常 - 正确做法:先确认字节数组原始编码,再用对应
GetString得到字符串,再用目标编码的GetBytes输出 - 性能提示:反复调用
Encoding.UTF8.GetBytes不会新建对象,Encoding.UTF8是静态只读实例,可放心复用
常见错误信息:System.Text.DecoderFallbackException: Unable to translate bytes [...] at index X from specified code page to Unicode. —— 这说明你用错了 GetString 的编码参数,字节流根本不是你声称的那种编码。
Unicode 字面量 \uXXXX 和实际编码无关
"\u4F60\u597D" 这种写法只是 C# 源码里的 Unicode 码点表示,编译后就和普通字符串一样,运行时仍是 UTF-16 存储。它不决定你读写文件时用什么编码,也不影响 GetBytes 的输出结果。
- 也就是说:
Encoding.UTF8.GetBytes("\u4F60\u597D")和Encoding.UTF8.GetBytes("你好")完全等价 - 但
Encoding.ASCII.GetBytes("\u4F60\u597D")会把每个中文转成?,因为 ASCII 只认 0–127 - 别指望靠 \u 来“绕过编码问题”——它解决不了文件读写、网络传输中的编码不匹配
真正容易被忽略的点:编码转换失败时,默认 fallback 行为是静默替换为 ?,而不是报错。这意味着你可能在生产环境跑了很久才发现部分中文被悄悄吃掉了——务必在关键路径上显式配置 EncoderExceptionFallback 并捕获异常。











