不应使用 Encoding.Default...
一些答案使用Encoding.Default
,但微软对此提出警告:
不同的计算机可以使用不同的编码作为默认值,并且默认编码可以在单台计算机上更改。如果您使用默认编码对计算机之间流式传输的数据进行编码和解码,或在同一台计算机上的不同时间检索数据,则可能会错误地转换该数据。此外,Default 属性返回的编码使用 best-fit fallback [即编码完全搞砸了,所以你不能重新编码它]将不支持的字符映射到代码页支持的字符。由于这些原因,不建议使用默认编码。为确保正确解码编码字节,您应该使用 Unicode 编码,例如 UTF8Encoding 或 UnicodeEncoding。您还可以使用更高级别的协议来确保使用相同的格式进行编码和解码。
要检查默认编码是什么,请使用Encoding.Default.WindowsCodePage
(在我的情况下为 1250 - 遗憾的是,没有预定义的 CP1250 编码类,但可以将对象检索为Encoding.GetEncoding(1250)
)。
...应该改用UTF-8/UTF-16LE编码...
Encoding.ASCII
得分最高的答案是 7bit,所以它也不起作用,就我而言:
byte[] pass = Encoding.ASCII.GetBytes("šarže");
Console.WriteLine(Encoding.ASCII.GetString(pass)); // ?ar?e
按照微软的建议:
var utf8 = new UTF8Encoding();
byte[] pass = utf8.GetBytes("šarže");
Console.WriteLine(utf8.GetString(pass)); // šarže
Encoding.UTF8
别人推荐的是UTF-8编码的一个实例,也可以直接使用或者作为
var utf8 = Encoding.UTF8 as UTF8Encoding;
Encoding.Unicode
在内存中的字符串表示中很受欢迎,因为它使用固定的每个字符 2 个字节,因此可以在恒定时间内以更多的内存使用为代价跳转到第 n 个字符:它是 UTF-16LE。在 MSVC# 中,*.cs 文件默认为 UTF-8 BOM,其中的字符串常量在编译时转换为 UTF-16LE(请参阅@OwnagesMagic 注释),但未将其定义为默认值:StreamWriter等许多类使用 UTF -8 作为默认值。
...但它并不总是使用
默认编码具有误导性:.NET 在任何地方都使用 UTF-8(包括在源代码中硬编码的字符串)和 UTF-16LE ( )将字符串存储在内存中,但WindowsEncoding.Unicode
实际上使用 2 个其他非 UTF8 默认值:.NET 之前)和OEM 代码页(又名 DOS 标准)。这些因国家/地区而异(例如,Windows 捷克版使用 CP1250 和 CP852)并且通常在 Windows API 库中进行硬编码。因此,如果您只是将 UTF-8 设置为控制台(因为 .NET 隐式执行并假装它是默认设置)并运行一些本地化命令(如 ping),它可以在英文版本中运行,但您会在捷克共和国获得豆腐文本。chcp 65001
让我分享一下我的真实经历:我创建了 WinForms 应用程序,为教师定制了 git 脚本。输出是通过 Microsoft 描述为(我添加的粗体文本)的过程在后台异步获得的:
在此上下文中,“shell”一词(UseShellExecute)指的是图形 shell (ANSI CP)(类似于 Windows shell)而不是命令 shell(例如 bash 或 sh)(OEM CP),它允许用户启动图形应用程序或打开文档(在非美国环境中输出混乱)。
因此有效地 GUI 默认为 UTF-8,进程默认为 CP1250,控制台默认为 852。所以输出在 852 中解释为 UTF-8,解释为 CP1250。我得到了豆腐文本,由于双重转换,我无法从中推断出原始代码页。我花了一周的时间来弄清楚为进程脚本显式设置 UTF-8 并在主线程中将输出从 CP1250 转换为 UTF-8。现在它在东欧工作,但西欧 Windows 使用 1252。ANSI CP 不容易确定,因为许多命令systeminfo
也已本地化,其他方法因版本而异:在这种环境中可靠地显示国家字符几乎是不可行的。
因此,直到 21 世纪下半叶,请不要使用任何“默认代码页”并明确设置(如果可能,设置为 UTF-8 或 UTF-16LE)。