了解编码
更新:2007 年 11 月
.NET Framework 在内部将文本存储为 Unicode UTF-16 格式。编码器将此文本数据转换成字节序列。解码器将字节序列转换成此内部格式。编码描述了编码器或解码器的操作规则。例如,UTF8Encoding 类描述了对以 Unicode UTF-8 格式表示的文本的字节序列的编码和解码规则。编码和解码过程还可以包含某些验证步骤。例如,UnicodeEncoding 类会检查所有代理项,以确保它们构成有效的代理项对。这两个类都是从 Encoding 类继承的。
选择编码
Unicode 标准为所支持的每个脚本中的每个字符分配一个码位(一个数字)。Unicode 转换格式 (UTF) 是一种码位编码方式。有关 System.Text 中的类所支持的 UTF 的更多信息,请参见 .NET Framework 中的 Unicode 中的“使用 Unicode 编码”。
选择编码类
Encoding 类的使用非常广泛。使用受支持的继承自 Encoding 的类,.NET 应用程序可以与在旧式应用程序中可能遇到的常见编码一起工作,并且您还可以实现其他编码。不过,当您有机会选择编码时,强烈建议您使用 Unicode 编码,通常为 UTF8Encoding 或 UnicodeEncoding(也支持 UTF32Encoding)。尤其需要指出的是,UTF8Encoding 比 ASCIIEncoding 更为可取。如果内容是 ASCII,则这两种编码是相同的,但 UTF8Encoding 还可以表示每个 Unicode 字符,而 ASCIIEncoding 仅支持介于 U+0000 和 U+007F 之间的 Unicode 字符值。因为 ASCIIEncoding 不提供错误检测,所以从安全角度来看,UTF8Encoding 也更好些。
UTF8Encoding 已经过优化以尽可能提高速度,它应比任何其他编码更快速。即使对于完全采用 ASCII 的内容,使用 UTF8Encoding 执行的操作也比使用 ASCIIEncoding 执行的操作速度更快。只有对某些旧式应用程序,才应考虑使用 ASCIIEncoding。但即使在这种情况下,UTF8Encoding 可能仍然是一种更好的选择。假定采用默认设置,将会出现以下情形:
如果应用程序具有并非严格采用 ASCII 的内容,并使用 ASCIIEncoding 对其进行编码,则每个非 ASCII 字符都将被编码成一个问号(“?”)。如果应用程序随后对此数据进行解码,将会丢失信息。
如果应用程序具有并非严格采用 ASCII 的内容,并使用 UTF8Encoding 对其进行编码,那么在将结果解释为 ASCII 时,结果看起来是不可理解的。但如果应用程序随后对此数据进行解码,数据能够成功还原。
选择回退策略
如果应用程序尝试对字符进行编码或解码,但不存在相应的映射,则应用程序必须实施回退策略(这是一种失败处理机制)。有两种类型的回退策略:
最佳回退
当字符在目标编码/解码中不具有精确匹配时,应用程序可以尝试将它们映射到类似的字符。
替换字符串回退
如果不存在合适的类似字符,则应用程序可以指定替换字符串。
例如,应用程序可以调用 GetEncoding(1252, 0, 0)(请参见 GetEncoding)。此调用通过将 encoderFallback 和 decoderFallback 指定为零来指定代码页 1252(用于西欧语言的 Windows 代码页)。对于某些 Unicode 字符,默认行为是最佳映射。例如,在对带圆圈的大写拉丁字母 S (U+24C8) 进行编码之前,会将其更改为大写拉丁字母 S (U+0053),并且将上标 5 (U+2075) 更改为数字 5 (U+0035)。如果应用程序随后从代码页 1252 向 Unicode 进行解码,则字母周围的圆圈将丢失,25 将变成 25。其他转换产生的变化可能更大。例如,Unicode 无限大符号 (U+221E) 可能会映射成数字 8 (U+0038)。
最佳策略随代码页的不同而有所变化,在此不加详细记录。例如,对于有些代码页,全角拉丁字符会映射成更常见的半角拉丁字符。对于其他代码页,不进行这种映射。
即使在积极的最佳策略中,在有些编码中仍然找不到适合某些字符的映射。例如,中文汉字不能合理地映射到代码页 1252。在这种情况下,将使用替换字符串。默认情况下,此字符串只是一个问号 (U+003F)。
对于 Encoding,最佳映射是默认行为,它将 Unicode 数据编码成代码页数据,有些旧式应用程序依赖于此行为。但是出于安全考虑,大多数新的应用程序应避免采用最佳行为。例如,应用程序不应对域名进行最佳编码。
应用程序应使用最佳映射的以下替代方式:
仅使用 Unicode 编码(UTF8Encoding、UnicodeEncoding 和 UTF32Encoding)以避免回退问题。
警告: 尽管 UTF7Encoding 在技术上是一种 Unicode 编码,但它的可靠性和安全性低于其他编码。在某些情况下,更改一个位可能会完全改变对整个 UTF-7 字符串的解释。在其他情况下,大不相同的 UTF-7 字符串可能会编码成相同的文本。因此,如果有其他选择,不应使用 UTF-7。UTF-8 比 UTF-7 更为可取。
使用 EncoderExceptionFallback 和 DecoderExceptionFallback,如果不能准确映射字符,将引发异常(分别为 EncoderFallbackException 和 DecoderFallbackException)。
如果字符不能精确映射,请总是使用 EncoderReplacementFallback 和 DecoderReplacementFallback 来取代替换字符串。对于 ASCIIEncoding,这是默认行为。默认情况下,此字符串只是一个问号,但提供了允许应用程序选择不同字符串的方法。尽管这通常是单个字符,但并不要求如此。对于在将文本转换成 Unicode 时使用的 DecoderReplacementFallback,通常使用的一个字符是替换字符 (U+FFFD)。
请使用自定义的 EncoderFallback 和/或 DecoderFallback 来实现首选策略。请参见 “回退”编码应用程序示例。
以下是对最佳编码(或解码)回退策略的两点进一步说明:
在大多数情况下,最佳映射是一个编码问题而不是一个解码问题。有极少数代码页包含无法成功映射到 Unicode 的字符。因为这些字符不常用,所以 Unicode 中省略了它们。
不存在对应于最佳回退的受支持的命名对象。每个代码页的最佳回退机制都各不相同。如果对于单个 Encoding 对象,应用程序需要在最佳回退和其他某个回退之间来回切换,则它应在对其他任何回退对象进行赋值之前,将原始的最佳回退对象复制到一个变量。然后,应用程序可以通过将该值重新赋给 Encoding.EncoderFallback 或 Encoding.DecoderFallback,恢复最佳回退。
请参见
任务
参考
Decoding