字符是可以通过多种不同方式表示的抽象实体。 字符编码是一个支持字符集中的每个字符进行配对值表示该字符的系统。 例如,摩尔斯电码是一种为罗马字母表中的每个字符与点的模式和短划线适用于在电报线路中传输的字符编码。 计算机的字符编码对一个支持字符集中的每个字符与表示该字符的数值。 字符编码具有两个不同的组件:
-
编码器,将字符序列转换为数值序列 (字节)。
-
解码器,将字节序列转换为字符序列。
字符编码描述了编码器和解码器运行的规则。 例如, UTF8Encoding 类描述了编码的规则对和解密时, 8 位 Unicode 格式 (utf-8),使用表示单个 Unicode 字符的一到四个字节。 编码和解码还可以包括验证。 例如, UnicodeEncoding 类检查所有代理以确保它们组成有效的代理项对。 (代理对包含代码中的字符点从 U+D800 范围到后面的字符 U+DBFF 的范围到 U+DFFF。) 回退策略确定编码器如何处理无效字符或解码器如何处理无效字节。
警告 |
---|
.NET framework 编码类提供了一种存储和转换字符数据的方法。 不应使用这些类存储二进制数据以字符串的形式。 根据所使用的编码,将二进制数据转换为使用编码类的字符串格式可能引入意外行为并导致错误或损坏的数据。 若要将二进制数据转换为字符串形式,请使用 Convert..::.ToBase64String 方法。 |
面向公共语言运行时使用编码器映射 Unicode 字符表示的应用程序在公共语言运行时支持到其他编码模式。 它们使用解码器将字符从非 Unicode 编码映射为 Unicode。
本主题包含以下几节:
-
Encodings in the .NET Framework
-
Selecting an Encoding Class
-
Using an Encoding Object
-
Choosing a Fallback Strategy
-
Implementing a Custom Fallback Strategy
.NET framework 中的所有字符编码类从 System.Text..::.Encoding 类继承,这是一个抽象类定义的通用功能所有字符编码。 访问 .NET framework 中实现的单个编码对象,请执行以下操作:
-
使用 Encoding 类的静态属性,这些属性会返回表示标准字符编码在 .NET framework 中提供的 (ASCII、 UTF-7、 UTF-8、 UTF-16 和 UTF-32)。 例如, Encoding..::.Unicode 属性返回 UnicodeEncoding 对象。 每个对象都使用替换回退来处理它无法编码的字符串和无法解码的字节。 (有关更多信息,请参见 Replacement Fallback 一节。)
-
调用编码的类构造函数。 ASCII、 UTF-7、 UTF-8, UTF-16 和 UTF-32 编码的对象可通过实例化。 默认情况下,每个对象都使用替换回退来处理它无法解码,但是,您可以指定的字符串和字节应引发异常。 (有关更多信息,请参见 Replacement Fallback 和 Exception Fallback 节。)
-
调用 Encoding..::.Encoding(Int32) 构造函数并向其传递表示编码的整数。 标准编码对象使用替换回退,因此,代码页和双字节字符集 (dbcs) 编码对象使用最佳回退到它们无法编码的处理它们无法编码的字符串和无法解码的字节。 (有关更多信息,请参见 Best-Fit Fallback 一节。)
-
调用 Encoding..::.GetEncoding 方法,返回所有标准、代码页或 DBCS 编码在 .NET framework。 重载允许您为编码器和解码器指定回退对象。
说明 |
---|
Unicode 标准分配码位 (一个数字) 和名称为所支持的每个脚本的每个字符。 例如,字符 “A”由码位 U+0041 和名称 “LATIN CAPITAL LETTER A”。 Unicode 转换格式 (UTF)编码定义方式输入码位到一个或多个字节序列。 ,因为它在单个编码,允许从任何字符集中的字符表示 Unicode 编码方案简化全球通用的应用程序开发。 应用程序开发人员不必再跟踪用于生成特定语言或编写系统字符的编码方案,并且,数据可以在世界上的各系统之间共享,而不会受到损坏。 .NET framework 支持 Unicode 标准定义的三种编码:UTF-8、 UTF-16 和 UTF-32。 有关更多信息,请参见 Unicode 标准 Unicode 主页。 |
可以检索有关所有编码的信息。在 .NET framework 通过调用 Encoding..::.GetEncodings 方法。 .NET framework 支持下表中列出的字符编码系统。
编码 | 类 | 说明 | 优点/缺点 |
---|---|---|---|
ASCII | ASCIIEncoding | 使用字节较低的七位,对有限的字符。 | 由于此编码仅支持从 U+0000 到 U+007F 的字符值,在许多情况下它对于国际化应用程序不够用的。 |
UTF-7 | UTF7Encoding | 字符表示为 7 位 ASCII 字符序列。 非 ASCII Unicode 字符的 ASCII 字符转义序列表示。 | UTF-7 支持协议 (如电子邮件和新闻组协议。 但是, UTF-7 并非特别安全或可靠。 在某些情况下,更改一个位可能会完全改变对整个 UTF-7 字符串的解释。 在某些情况下,不同的 UTF-7 字符串可能编码成相同的文本。 对于包含非 ASCII 字符的序列, UTF-7 要高于 UTF-8 需要更多的空间,并且,编码/解码速度也更慢。 因此,应该尽可能使用 UTF-8 而不是 UTF-7。 |
UTF-8 | UTF8Encoding | 每个 Unicode 码位表示为一到四个字节序列。 | UTF-8 支持 8 位数据大小和使用许多现有操作系统。 对于 ASCII 范围的字符, UTF-8 与 ASCII 编码相同并且允许更宽的字符集。 但是,对于中文 - 日语 (CJK) - 朝鲜语 (cjk) 脚本, UTF-8 可能超过 UTF-16 要求每个字符使用三个字节,这可能导致较大的数据范围。 请注意,有时 ASCII 数据 (如 HTML 标记, " CJK 范围增大的范围。 |
UTF-16 | UnicodeEncoding | 每个 Unicode 码位表示为一个或两个 16 位整数序列。 最常见的 Unicode 字符只需要一个 UTF-16 码位,不过, Unicode 增补字符 (U+10000 及更高) 需要两个 UTF-16 代理。 little-endian 和 big-endian 字节顺序支持。 | UTF-16 公共语言运行时用来编码表示 Char 和 String 值,并且, windows 操作系统使用它表示 WCHAR 值。 |
UTF-32 | UTF32Encoding | 每个 Unicode 码位表示为 32 位整数。 little-endian 和 big-endian 字节顺序支持。 | 可以使用 UTF-32 编码,当应用程序若要避免代理项码位在 UTF-16 编码空间太重要的操作系统时的编码行为。 在显示呈现的单个标志符号多个多个 UTF-32 字符。 |
ANSI/ISO 编码 | 提供对各种代码页的支持。 在 windows 操作系统上,代码页用于支持特定语言或组。 对列表的表受 .NET framework 支持的代码页,请参见 Encoding 类。 通过调用 Encoding..::.GetEncoding(Int32) 方法检索特定代码页的编码对象。 | 代码页包含码位、 256 和是从零开始的。 在大多数代码页, 0 到 127 的码位表示 ASCII 字符集,,而 128 到 255 之间的码位在代码页之间。 例如,代码页 1252 用于拉丁语书写系统提供字符,包括英语、德语和法语。 代码页 1252 中的后 128 个码位均包含重音字符。 代码页 1253 提供在希腊语书写系统需要的字符代码。 代码页 1253 中的后 128 包含希腊语字符。 因此,依赖 ANSI 代码页的应用程序在同一文本流不能将希腊语和德语存储,除非它包含指示引用的代码页的标识符。 | |
双字节字符集 (dbcs) 编码 | 支持语言,如中文、日语和朝鲜语,超过 256 个字符。 在 DBCS 中,对码位 (一个双字节) 表示一个字符。 Encoding..::.IsSingleByte 属性返回 DBCS 编码的 false 。 通过调用 Encoding..::.GetEncoding(Int32) 方法检索特定 DBCS 的编码对象。 | 在 DBCS 中,对码位 (一个双字节) 表示一个字符。 当应用程序处理 DBCS 数据, DBCS 字符 (前导字节) 的第一个字节处理以及紧跟其后的结尾字节。 因为一对双字节码位可以将代码页表示不同的字符,则此方案依然不允许两种语言的组合,如日语和中文),在同一数据流。 |
这些编码使您能够使用 Unicode 字符以及最常用于在传统应用的编码。 此外,您还可以通过定义从 Encoding 并重写其成员派生的类来创建自定义编码。
如果您有机会选择应用程序要使用的编码,则应使用 Unicode 编码,最好是 UTF8Encoding 或 UnicodeEncoding。 (.NET framework 还支持第三个 Unicode 编码, UTF32Encoding。)
如果您计划使用 ASCII 编码 (ASCIIEncoding),选择 UTF8Encoding 。 这两个编码对于 ASCII 字符集相同的,但是, UTF8Encoding 具有以下优点:
-
它可以表示每个 Unicode 字符,,而 ASCIIEncoding 支持介于 U+0000 和 U+007F 之间的某些 Unicode 字符值。
-
它可以提供错误检测和更高的安全性。
-
它比其他编码调整尽快是应 express。 对于完全采用 ASCII 的内容,使用执行的操作 UTF8Encoding 比使用执行的操作速度更快 ASCIIEncoding。
仅使用 ASCIIEncoding 传统应用程序,应考虑。 但是,即使对旧应用程序, UTF8Encoding 可能是下列原因而言更好的选择:
-
如果应用程序具有未严格采用 ASCII 并使用对其与 ASCIIEncoding的内容,每个非 ASCII 字符输入作为问号 (?)。 如果应用程序随后对此数据进行解码,将会丢失信息。
-
如果应用程序具有未严格采用 ASCII 并使用对其与 UTF8Encoding的内容,该结果看起来理解,如果解释为 ASCII。 但是,因此,如果应用程序随后使用 UTF-8 解码器对此数据进行解码,数据将成功进行往返。
在 Web 应用程序,字符发送到客户端以响应 web 请求应反映客户端上使用的编码。 在大多数情况下,您应该将 HttpResponse..::.ContentEncoding 属性设置为 HttpRequest..::.ContentEncoding 属性返回的值显示在用户期望的编码的文本。
编码器将字符串 (通常, Unicode 字符) 转换为其数字 (字节) 等效值。 例如,您可以使用 ASCII 编码器将 Unicode 字符转换为 ASCII,以便可以显示在控制台。 若要执行转换,则调用 Encoding..::.GetBytes 方法。 如果要确定有多少字节是在执行编码以前需要存储编码字符,可以调用 GetByteCount 方法。
下面的示例使用单字节数组在两个不同操作的字符串。 它包含指示在字节数组中的起始位置的下一组 ASCII 编码的字节的索引。 它调用 ASCIIEncoding..::.GetByteCount(String) 方法确保字节数组足以容纳编码的字符串。 然后调用方法 ASCIIEncoding..::.GetBytes(String, Int32, Int32, array<Byte>[]()[], Int32) 输入该字符串中的字符。
Imports System.TextModule ExamplePublic Sub Main()Dim strings() As String = { "This is the first sentence. ", "This is the second sentence. " }Dim asciiEncoding As Encoding = Encoding.ASCII' Create array of adequate size.Dim bytes(50) As Byte' Create index for current position of array.Dim index As Integer = 0Console.WriteLine("Strings to encode:")For Each stringValue In stringsConsole.WriteLine(" {0}", stringValue)Dim count As Integer = asciiEncoding.GetByteCount(stringValue)If count + index >= bytes.Length ThenArray.Resize(bytes, bytes.Length + 50)End IfDim written As Integer = asciiEncoding.GetBytes(stringValue, 0, stringValue.Length, bytes, index) index = index + written Next Console.WriteLine()Console.WriteLine("Encoded bytes:")Console.WriteLine("{0}", ShowByteValues(bytes, index))Console.WriteLine()' Decode Unicode byte array to a string.Dim newString As String = asciiEncoding.GetString(bytes, 0, index)Console.WriteLine("Decoded: {0}", newString)End SubPrivate Function ShowByteValues(bytes As Byte(), last As Integer) As StringDim returnString As String = " "For ctr As Integer = 0 To last - 1If ctr Mod 20 = 0 Then returnString += vbCrLf + " "returnString += String.Format("{0:X2} ", bytes(ctr))NextReturn returnStringEnd Function End Module ' The example displays the following output: ' Strings to encode: ' This is the first sentence. ' This is the second sentence. ' ' Encoded bytes: ' ' 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65 ' 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20 ' 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20 ' ' Decoded: This is the first sentence. This is the second sentence.
using System; using System.Text;public class Example {public static void Main(){string[] strings= { "This is the first sentence. ", "This is the second sentence. " };Encoding asciiEncoding = Encoding.ASCII;// Create array of adequate size.byte[] bytes = new byte[49];// Create index for current position of array.int index = 0;Console.WriteLine("Strings to encode:");foreach (var stringValue in strings) {Console.WriteLine(" {0}", stringValue);int count = asciiEncoding.GetByteCount(stringValue);if (count + index >= bytes.Length)Array.Resize(ref bytes, bytes.Length + 50);int written = asciiEncoding.GetBytes(stringValue, 0, stringValue.Length, bytes, index); index = index + written; } Console.WriteLine("\nEncoded bytes:");Console.WriteLine("{0}", ShowByteValues(bytes, index));Console.WriteLine();// Decode Unicode byte array to a string.string newString = asciiEncoding.GetString(bytes, 0, index);Console.WriteLine("Decoded: {0}", newString);}private static string ShowByteValues(byte[] bytes, int last ) {string returnString = " ";for (int ctr = 0; ctr <= last - 1; ctr++) {if (ctr % 20 == 0)returnString += "\n ";returnString += String.Format("{0:X2} ", bytes[ctr]);}return returnString;} } // The example displays the following output: // Strings to encode: // This is the first sentence. // This is the second sentence. // // Encoded bytes: // // 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65 // 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20 // 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20 // // Decoded: This is the first sentence. This is the second sentence.
解码器将反映特定字符编码到字符集,在字符数组或字符串的字节数组。 若要将字节数组解码为字符数组,可调用 Encoding..::.GetChars 方法。 若要将字节数组解码为字符串,可调用 GetString 方法。 如果要确定有多少字符是在执行解码以前需要存储已解码的字节,可以调用 GetCharCount 方法。
下面的示例先对三个字符串进行到单个字符数组。 它包含指示在字符数组中的起始位置的下一组解码字符的索引。 它调用 GetCharCount 方法确保字符数组足以容纳所有解码字符。 然后调用 ASCIIEncoding..::.GetChars(array<Byte>[]()[], Int32, Int32, array<Char>[]()[], Int32) 方法解码字节数组。
Imports System.TextModule ExamplePublic Sub Main()Dim strings() As String = { "This is the first sentence. ", "This is the second sentence. ","This is the third sentence. " }Dim asciiEncoding As Encoding = Encoding.ASCII' Array to hold encoded bytes.Dim bytes() As Byte' Array to hold decoded characters.Dim chars(50) As Char' Create index for current position of character array.Dim index As Integer For Each stringValue In stringsConsole.WriteLine("String to Encode: {0}", stringValue)' Encode the string to a byte array.bytes = asciiEncoding.GetBytes(stringValue)' Display the encoded bytes.Console.Write("Encoded bytes: ")For ctr As Integer = 0 To bytes.Length - 1Console.Write(" {0}{1:X2}", If(ctr Mod 20 = 0, vbCrLf, ""), bytes(ctr))Next Console.WriteLine()' Decode the bytes to a single character array.Dim count As Integer = asciiEncoding.GetCharCount(bytes)If count + index >= chars.Length ThenArray.Resize(chars, chars.Length + 50)End IfDim written As Integer = asciiEncoding.GetChars(bytes, 0, bytes.Length, chars, index) index = index + writtenConsole.WriteLine() Next' Instantiate a single string containing the characters.Dim decodedString As New String(chars, 0, index - 1)Console.WriteLine("Decoded string: ")Console.WriteLine(decodedString)End Sub End Module ' The example displays the following output: ' String to Encode: This is the first sentence. ' Encoded bytes: ' 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65 ' 6E 74 65 6E 63 65 2E 20 ' ' String to Encode: This is the second sentence. ' Encoded bytes: ' 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73 ' 65 6E 74 65 6E 63 65 2E 20 ' ' String to Encode: This is the third sentence. ' Encoded bytes: ' 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65 ' 6E 74 65 6E 63 65 2E 20 ' ' Decoded string: ' This is the first sentence. This is the second sentence. This is the third sentence.
using System; using System.Text;public class Example {public static void Main(){string[] strings = { "This is the first sentence. ", "This is the second sentence. ","This is the third sentence. " };Encoding asciiEncoding = Encoding.ASCII;// Array to hold encoded bytes.byte[] bytes;// Array to hold decoded characters.char[] chars = new char[50];// Create index for current position of character array.int index = 0; foreach (var stringValue in strings) {Console.WriteLine("String to Encode: {0}", stringValue);// Encode the string to a byte array.bytes = asciiEncoding.GetBytes(stringValue);// Display the encoded bytes.Console.Write("Encoded bytes: ");for (int ctr = 0; ctr < bytes.Length; ctr++)Console.Write(" {0}{1:X2}", ctr % 20 == 0 ? Environment.NewLine : "", bytes[ctr]);Console.WriteLine();// Decode the bytes to a single character array.int count = asciiEncoding.GetCharCount(bytes);if (count + index >= chars.Length)Array.Resize(ref chars, chars.Length + 50);int written = asciiEncoding.GetChars(bytes, 0, bytes.Length, chars, index); index = index + written;Console.WriteLine(); }// Instantiate a single string containing the characters.string decodedString = new string(chars, 0, index - 1);Console.WriteLine("Decoded string: ");Console.WriteLine(decodedString);} } // The example displays the following output: // String to Encode: This is the first sentence. // Encoded bytes: // 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65 // 6E 74 65 6E 63 65 2E 20 // // String to Encode: This is the second sentence. // Encoded bytes: // 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73 // 65 6E 74 65 6E 63 65 2E 20 // // String to Encode: This is the third sentence. // Encoded bytes: // 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65 // 6E 74 65 6E 63 65 2E 20 // // Decoded string: // This is the first sentence. This is the second sentence. This is the third sentence.
从 Encoding 派生的类的编码和解码方法旨在完整的数据集;即要编码或解码的所有数据在单个方法调用中会提供 但是,在某些情况下,流,因此,要编码或解码的数据可能只来自单独的读取操作。 这需要编码或解码操作确保从以前的调用中所有已保存的状态。 从 Encoder 派生的类的方法和 Decoder 能够处理编码,并且范围的解码操作多个方法调用。
特定编码的一 Encoder 对象来自此编码的 Encoding..::.GetEncoder 属性可用。 特定编码的一 Decoder 对象来自此编码的 Encoding..::.GetDecoder 属性可用。 对于解码操作,请注意从 Decoder 派生的类包括一个 Decoder..::.GetChars 方法,但是,它们没有对应于 Encoding..::.GetString的方法。
下面的示例阐释 Encoding..::.GetChars 和 Decoder..::.GetChars 方法之间的差异解码 Unicode 字节数组。 该示例输入包含某些 Unicode 字符到文件的字符串,然后使用这两种解码方法一次性解码其中十个字节。 因为代理对出现在第十,第十一个字节,所以在单独的方法调用。 如输出所示, Encoding..::.GetChars 方法不能正确解码字节和不用 U+FFFD (REPLACEMENT CHARACTER) 替换它们。 另一方面, Decoder..::.GetChars 方法成功解码字节数组获取原始字符串。
Imports System.IO Imports System.TextModule ExamplePublic Sub Main()' Use default replacement fallback for invalid encoding.Dim enc As New UnicodeEncoding(True, False, False)' Define a string with various Unicode characters.Dim str1 As String = String.Format("AB YZ 19 {0}{1} {2}", ChrW(&hD800), ChrW(&hDC05), ChrW(&h00e4))str1 += String.Format("Unicode characters. {0} {1} s {2}{3}", ChrW(&h00a9), ChrW(&h010C), ChrW(&h0062), ChrW(&h0308))Console.WriteLine("Created original string...")Console.WriteLine()' Convert string to byte array. Dim bytes() As Byte = enc.GetBytes(str1)Dim fs As FileStream = File.Create(".\characters.bin")Dim bw As New BinaryWriter(fs)bw.Write(bytes)bw.Close()' Read bytes from file.Dim fsIn As FileStream = File.OpenRead(".\characters.bin")Dim br As New BinaryReader(fsIn)Const count As Integer = 10 ' Number of bytes to read at a time. Dim bytesRead(9) As Byte ' Buffer (byte array).Dim read As Integer ' Number of bytes actually read. Dim str2 As String = "" ' Decoded string.' Try using Encoding object for all operations.Do read = br.Read(bytesRead, 0, count)str2 += enc.GetString(bytesRead, 0, read) Loop While read = countbr.Close()Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...")CompareForEquality(str1, str2)Console.WriteLine()' Use Decoder for all operations.fsIn = File.OpenRead(".\characters.bin")br = New BinaryReader(fsIn)Dim decoder As Decoder = enc.GetDecoder()Dim chars(50) As CharDim index As Integer = 0 ' Next character to write in array.Dim written As Integer = 0 ' Number of chars written to array.Do read = br.Read(bytesRead, 0, count)If index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length Then Array.Resize(chars, chars.Length + 50)End If written = decoder.GetChars(bytesRead, 0, read, chars, index)index += written Loop While read = countbr.Close() ' Instantiate a string with the decoded characters.Dim str3 As New String(chars, 0, index) Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...")CompareForEquality(str1, str3) End SubPrivate Sub CompareForEquality(original As String, decoded As String)Dim result As Boolean = original.Equals(decoded)Console.WriteLine("original = decoded: {0}", original.Equals(decoded, StringComparison.Ordinal))If Not result ThenConsole.WriteLine("Code points in original string:")For Each ch In originalConsole.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))NextConsole.WriteLine()Console.WriteLine("Code points in decoded string:")For Each ch In decodedConsole.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))NextConsole.WriteLine()End IfEnd Sub End Module ' The example displays the following output: ' Created original string... ' ' Decoded string using UnicodeEncoding.GetString()... ' original = decoded: False ' Code points in original string: ' 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F ' 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C ' 0020 0073 0020 0062 0308 ' Code points in decoded string: ' 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F ' 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C ' 0020 0073 0020 0062 0308 ' ' Decoded string using UnicodeEncoding.Decoder.GetString()... ' original = decoded: True
using System; using System.IO; using System.Text;public class Example {public static void Main(){// Use default replacement fallback for invalid encoding.UnicodeEncoding enc = new UnicodeEncoding(true, false, false);// Define a string with various Unicode characters.string str1 = "AB YZ 19 \uD800\udc05 \u00e4"; str1 += "Unicode characters. \u00a9 \u010C s \u0062\u0308"; Console.WriteLine("Created original string...\n");// Convert string to byte array. byte[] bytes = enc.GetBytes(str1);FileStream fs = File.Create(@".\characters.bin");BinaryWriter bw = new BinaryWriter(fs);bw.Write(bytes);bw.Close();// Read bytes from file.FileStream fsIn = File.OpenRead(@".\characters.bin");BinaryReader br = new BinaryReader(fsIn);const int count = 10; // Number of bytes to read at a time. byte[] bytesRead = new byte[10]; // Buffer (byte array).int read; // Number of bytes actually read. string str2 = String.Empty; // Decoded string.// Try using Encoding object for all operations.do { read = br.Read(bytesRead, 0, count);str2 += enc.GetString(bytesRead, 0, read); } while (read == count);br.Close();Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...");CompareForEquality(str1, str2);Console.WriteLine();// Use Decoder for all operations.fsIn = File.OpenRead(@".\characters.bin");br = new BinaryReader(fsIn);Decoder decoder = enc.GetDecoder();char[] chars = new char[50];int index = 0; // Next character to write in array.int written = 0; // Number of chars written to array.do { read = br.Read(bytesRead, 0, count);if (index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length) Array.Resize(ref chars, chars.Length + 50);written = decoder.GetChars(bytesRead, 0, read, chars, index);index += written; } while (read == count);br.Close(); // Instantiate a string with the decoded characters.string str3 = new String(chars, 0, index); Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...");CompareForEquality(str1, str3); }private static void CompareForEquality(string original, string decoded){bool result = original.Equals(decoded);Console.WriteLine("original = decoded: {0}", original.Equals(decoded, StringComparison.Ordinal));if (! result) {Console.WriteLine("Code points in original string:");foreach (var ch in original)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();Console.WriteLine("Code points in decoded string:");foreach (var ch in decoded)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();}} } // The example displays the following output: // Created original string... // // Decoded string using UnicodeEncoding.GetString()... // original = decoded: False // Code points in original string: // 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F // 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C // 0020 0073 0020 0062 0308 // Code points in decoded string: // 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F // 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C // 0020 0073 0020 0062 0308 // // Decoded string using UnicodeEncoding.Decoder.GetString()... // original = decoded: True
当方法尝试编码或解码字符时,如果映射不存在,则必须实现确定的回退方法应如何处理失败的映射。 有三种类型的回退策略:
-
最佳回退
-
替换回退
-
异常回退
重要事项 |
---|
,当 Unicode 字符无法映射到特定代码页编码时,在编码操作中最常见的问题。 ,当无效字节顺序不能转换为有效的 Unicode 字符时,在解码操作中最常见的问题。 因此,您应了解哪种回退策略特定编码对象使用。 只要有可能,,在实例化对象时,应指定编码对象使用的回退策略。 |
最佳回退
当字符在目标编码中不具有精确匹配项时,编码器可以尝试将其映射到类似的字符。 (最佳回退主要是编码而不是解码问题。 具有非常包含字符无法成功映射为 Unicode。) 的少量代码页 最佳回退由 Encoding..::.GetEncoding(Int32) 和 Encoding..::.GetEncoding(String) 重载检索代码页的默认和双字节字符集编码。
说明 |
---|
理论上, .NET framework 中提供的 Unicode 编码类 (UTF8Encoding、 UnicodeEncoding和 UTF32Encoding) 支持每个字符集中的每个字符,因此,它们可用于消除最佳回退问题。 |
最佳策略因代码页的不同而有所不同,因此,它们不详细说明。 例如,对于有些代码页,更为常见的半角拉丁字符的全角拉丁字符映射表。 对于其他代码页,不进行这种映射。 在一个积极的最佳策略下,不某些字符的映射的改编在有些编码。 例如,汉字不能适当地映射到代码页 1252。 在这种情况下,使用替换字符串。 默认情况下,此字符串是一个问号 (u+003f)。
第 1252 页 (用于西欧语言的 windows 代码页) 来说明最佳映射及其缺点下面的示例使用代码。 Encoding..::.GetEncoding(Int32) 方法用于检测代码页 1252 的编码对象。 默认情况下,它对它不支持的 Unicode 字符使用最佳映射。 该示例实例化包含三个非 ASCII 字符的字符串 (旋转的 LATIN CAPITAL LETTER S (U+24C8),这些五 (u+2075) 和 INFINITY (u+221e)) 由空格分隔的前缀。 如该示例输出所示,,在编码字符串时,原始三个非空格字符。问号 (u+003f), DIGIT FIVE (u+0035) 和 DIGIT EIGHT (u+0038) 来替换。 DIGIT EIGHT 是不受支持的 INFINITY 字符的最差的替换,,并且 QUESTION MARK 指示映射为原始字符不可用。
Imports System.TextModule ExamplePublic Sub Main()' Get an encoding for code page 1252 (Western Europe character set).Dim cp1252 As Encoding = Encoding.GetEncoding(1252)' Define and display a string.Dim str As String = String.Format("{0} {1} {2}", ChrW(&h24c8), ChrW(&H2075), ChrW(&h221E))Console.WriteLine("Original string: " + str)Console.Write("Code points in string: ")For Each ch In strConsole.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))NextConsole.WriteLine() Console.WriteLine()' Encode a Unicode string.Dim bytes() As Byte = cp1252.GetBytes(str)Console.Write("Encoded bytes: ")For Each byt In bytesConsole.Write("{0:X2} ", byt)NextConsole.WriteLine()Console.WriteLine()' Decode the string.Dim str2 As String = cp1252.GetString(bytes)Console.WriteLine("String round-tripped: {0}", str.Equals(str2))If Not str.Equals(str2) ThenConsole.WriteLine(str2)For Each ch In str2Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))NextEnd IfEnd Sub End Module ' The example displays the following output: ' Original string: Ⓢ ⁵ ∞ ' Code points in string: 24C8 0020 2075 0020 221E ' ' Encoded bytes: 3F 20 35 20 38 ' ' String round-tripped: False ' ? 5 8 ' 003F 0020 0035 0020 0038
using System; using System.Text;public class Example {public static void Main(){// Get an encoding for code page 1252 (Western Europe character set).Encoding cp1252 = Encoding.GetEncoding(1252);// Define and display a string.string str = "\u24c8 \u2075 \u221e";Console.WriteLine("Original string: " + str);Console.Write("Code points in string: ");foreach (var ch in str)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine("\n"); // Encode a Unicode string.Byte[] bytes = cp1252.GetBytes(str);Console.Write("Encoded bytes: ");foreach (byte byt in bytes)Console.Write("{0:X2} ", byt);Console.WriteLine("\n");// Decode the string.string str2 = cp1252.GetString(bytes);Console.WriteLine("String round-tripped: {0}", str.Equals(str2));if (! str.Equals(str2)) {Console.WriteLine(str2);foreach (var ch in str2)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));}} } // The example displays the following output: // Original string: Ⓢ ⁵ ∞ // Code points in string: 24C8 0020 2075 0020 221E // // Encoded bytes: 3F 20 35 20 38 // // String round-tripped: False // ? 5 8 // 003F 0020 0035 0020 0038
最佳映射是输入 Unicode 数据编码为代码页数据的 Encoding 对象的默认行为,并且,对于依赖此行为的旧版应用程序。 但出于安全原因,大多数新应用程序应避免最佳的行为。 例如,应用程序不应通过最佳编码对域名。
说明 |
---|
还可以实现自定义最佳回退映射为编码的。 有关更多信息,请参见部分 Implementing a Custom Fallback Strategy 。 |
如果最佳回退是编码对象的默认值,您可以选择另一种回退策略,当通过调用 Encoding..::.GetEncoding(Int32, EncoderFallback, DecoderFallback) 或 Encoding..::.GetEncoding(String, EncoderFallback, DecoderFallback) 重载检索 Encoding 对象时。 以下各节包括替换每个字符不能映射代码用星号页面 1252 上的一个示例 (*)。
Imports System.TextModule ExamplePublic Sub Main()Dim cp1252r As Encoding = Encoding.GetEncoding(1252, New EncoderReplacementFallback("*"),New DecoderReplacementFallback("*"))Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))Console.WriteLine(str1)For Each ch In str1Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))Next Console.WriteLine()Dim bytes() As Byte = cp1252r.GetBytes(str1)Dim str2 As String = cp1252r.GetString(bytes)Console.WriteLine("Round-trip: {0}", str1.Equals(str2))If Not str1.Equals(str2) ThenConsole.WriteLine(str2)For Each ch In str2Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))Next Console.WriteLine()End If End Sub End Module ' The example displays the following output: ' Ⓢ ⁵ ∞ ' 24C8 0020 2075 0020 221E ' Round-trip: False ' * * * ' 002A 0020 002A 0020 002A
using System; using System.Text;public class Example {public static void Main(){Encoding cp1252r = Encoding.GetEncoding(1252, new EncoderReplacementFallback("*"),new DecoderReplacementFallback("*"));string str1 = "\u24C8 \u2075 \u221E";Console.WriteLine(str1);foreach (var ch in str1)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();byte[] bytes = cp1252r.GetBytes(str1);string str2 = cp1252r.GetString(bytes);Console.WriteLine("Round-trip: {0}", str1.Equals(str2));if (! str1.Equals(str2)) {Console.WriteLine(str2);foreach (var ch in str2)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();} } } // The example displays the following output: // Ⓢ ⁵ ∞ // 24C8 0020 2075 0020 221E // Round-trip: False // * * * // 002A 0020 002A 0020 002A
替换回退
当字符在目标方案中不具有精确匹配,但是,如果没有合适的字符它可以映射到,应用程序可以指定替换字符或字符串。 这是 Unicode 解码器的默认行为,替换所有双字节序列它无法解码用 REPLACEMENT_CHARACTER (u+fffd)。 它也是 ASCIIEncoding 类的默认行为,替换每个字符它无法编码或解码使用问号。 下面的示例演示 Unicode 字符串的字符替换上。 如输出所示,无法解码为 ASCII 字节值的每个字符被 0x3F 替换,是问号的 ASCII 代码。
Imports System.TextModule ExamplePublic Sub Main()Dim enc As Encoding = Encoding.AsciiDim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))Console.WriteLine(str1)For Each ch In str1Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))Next Console.WriteLine()Console.WriteLine() ' Encode the original string using the ASCII encoder.Dim bytes() As Byte = enc.GetBytes(str1)Console.Write("Encoded bytes: ")For Each byt In bytesConsole.Write("{0:X2} ", byt)NextConsole.WriteLine()Console.WriteLine()' Decode the ASCII bytes.Dim str2 As String = enc.GetString(bytes)Console.WriteLine("Round-trip: {0}", str1.Equals(str2))If Not str1.Equals(str2) ThenConsole.WriteLine(str2)For Each ch In str2Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))Next Console.WriteLine()End If End Sub End Module ' The example displays the following output: ' Ⓢ ⁵ ∞ ' 24C8 0020 2075 0020 221E ' ' Encoded bytes: 3F 20 3F 20 3F ' ' Round-trip: False ' ? ? ? ' 003F 0020 003F 0020 003F
using System; using System.Text;public class Example {public static void Main(){Encoding enc = Encoding.ASCII;string str1 = "\u24C8 \u2075 \u221E";Console.WriteLine(str1);foreach (var ch in str1)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine("\n");// Encode the original string using the ASCII encoder.byte[] bytes = enc.GetBytes(str1);Console.Write("Encoded bytes: ");foreach (var byt in bytes)Console.Write("{0:X2} ", byt);Console.WriteLine("\n");// Decode the ASCII bytes.string str2 = enc.GetString(bytes);Console.WriteLine("Round-trip: {0}", str1.Equals(str2));if (! str1.Equals(str2)) {Console.WriteLine(str2);foreach (var ch in str2)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();} } } // The example displays the following output: // Ⓢ ⁵ ∞ // 24C8 0020 2075 0020 221E // // Encoded bytes: 3F 20 3F 20 3F // // Round-trip: False // ? ? ? // 003F 0020 003F 0020 003F
.NET framework 包括 EncoderReplacementFallback 和 DecoderReplacementFallback 类,并替换字符串; 如果字符在编码错误地映射或解码操作。 默认情况下,此替换字符串是问号,但是,可以调用类构造函数重载选择不同的字符串。 通常,替换字符串是单个字符,不过,这不是必需的。 下面的示例通过实例化将星号 ( EncoderReplacementFallback 对象更改代码页 1252 编码器的行为 (*) 用作替换字符串。
Imports System.TextModule ExamplePublic Sub Main()Dim cp1252r As Encoding = Encoding.GetEncoding(1252, New EncoderReplacementFallback("*"),New DecoderReplacementFallback("*"))Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))Console.WriteLine(str1)For Each ch In str1Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))Next Console.WriteLine()Dim bytes() As Byte = cp1252r.GetBytes(str1)Dim str2 As String = cp1252r.GetString(bytes)Console.WriteLine("Round-trip: {0}", str1.Equals(str2))If Not str1.Equals(str2) ThenConsole.WriteLine(str2)For Each ch In str2Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))Next Console.WriteLine()End If End Sub End Module ' The example displays the following output: ' Ⓢ ⁵ ∞ ' 24C8 0020 2075 0020 221E ' Round-trip: False ' * * * ' 002A 0020 002A 0020 002A
using System; using System.Text;public class Example {public static void Main(){Encoding cp1252r = Encoding.GetEncoding(1252, new EncoderReplacementFallback("*"),new DecoderReplacementFallback("*"));string str1 = "\u24C8 \u2075 \u221E";Console.WriteLine(str1);foreach (var ch in str1)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();byte[] bytes = cp1252r.GetBytes(str1);string str2 = cp1252r.GetString(bytes);Console.WriteLine("Round-trip: {0}", str1.Equals(str2));if (! str1.Equals(str2)) {Console.WriteLine(str2);foreach (var ch in str2)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();} } } // The example displays the following output: // Ⓢ ⁵ ∞ // 24C8 0020 2075 0020 221E // Round-trip: False // * * * // 002A 0020 002A 0020 002A
说明 |
---|
您还可以为编码实现替换类。 有关更多信息,请参见部分 Implementing a Custom Fallback Strategy 。 |
除了 QUESTION MARK (u+003f) 以外, Unicode REPLACEMENT CHARACTER (u+fffd) 通常用作替换字符串,,尤其是在解码无法成功转换为 Unicode 字符的字节序列时。 但是,您可以自由选择任何替换字符串,因此,它可以包含多个字符。
异常回退
而不是提供一个最佳回退或替换字符串,编码器会引发 EncoderFallbackException ,如果无法输入字符集,并且,解码器会引发 DecoderFallbackException ,如果无法解码字节数组。 若要在编码或解码操作中引发异常,可提供一 EncoderExceptionFallback 对象和一 DecoderExceptionFallback 对象,它们分别,对 Encoding..::.GetEncoding(String, EncoderFallback, DecoderFallback) 方法。 下面的示例声明与 ASCIIEncoding 类的异常回退。
Imports System.TextModule ExamplePublic Sub Main()Dim enc As Encoding = Encoding.GetEncoding("us-ascii", New EncoderExceptionFallback(), New DecoderExceptionFallback())Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))Console.WriteLine(str1)For Each ch In str1Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))Next Console.WriteLine()Console.WriteLine() ' Encode the original string using the ASCII encoder.Dim bytes() As Byte = {}Trybytes = enc.GetBytes(str1)Console.Write("Encoded bytes: ")For Each byt In bytesConsole.Write("{0:X2} ", byt)NextConsole.WriteLine()Catch e As EncoderFallbackExceptionConsole.Write("Exception: ")If e.IsUnknownSurrogate() ThenConsole.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.", Convert.ToUInt16(e.CharUnknownHigh), Convert.ToUInt16(e.CharUnknownLow), e.Index)ElseConsole.WriteLine("Unable to encode 0x{0:X4} at index {1}.", Convert.ToUInt16(e.CharUnknown), e.Index)End If Exit SubEnd TryConsole.WriteLine()' Decode the ASCII bytes.TryDim str2 As String = enc.GetString(bytes)Console.WriteLine("Round-trip: {0}", str1.Equals(str2))If Not str1.Equals(str2) ThenConsole.WriteLine(str2)For Each ch In str2Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))Next Console.WriteLine()End If Catch e As DecoderFallbackExceptionConsole.Write("Unable to decode byte(s) ")For Each unknown As Byte In e.BytesUnknownConsole.Write("0x{0:X2} ")NextConsole.WriteLine("at index {0}", e.Index)End TryEnd Sub End Module ' The example displays the following output: ' Ⓢ ⁵ ∞ ' 24C8 0020 2075 0020 221E ' ' Exception: Unable to encode 0x24C8 at index 0.
using System; using System.Text;public class Example {public static void Main(){Encoding enc = Encoding.GetEncoding("us-ascii", new EncoderExceptionFallback(), new DecoderExceptionFallback());string str1 = "\u24C8 \u2075 \u221E";Console.WriteLine(str1);foreach (var ch in str1)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine("\n");// Encode the original string using the ASCII encoder.byte[] bytes = {};try {bytes = enc.GetBytes(str1);Console.Write("Encoded bytes: ");foreach (var byt in bytes)Console.Write("{0:X2} ", byt);Console.WriteLine();}catch (EncoderFallbackException e) {Console.Write("Exception: ");if (e.IsUnknownSurrogate())Console.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.", Convert.ToUInt16(e.CharUnknownHigh), Convert.ToUInt16(e.CharUnknownLow), e.Index);elseConsole.WriteLine("Unable to encode 0x{0:X4} at index {1}.", Convert.ToUInt16(e.CharUnknown), e.Index);return;}Console.WriteLine();// Decode the ASCII bytes.try {string str2 = enc.GetString(bytes);Console.WriteLine("Round-trip: {0}", str1.Equals(str2));if (! str1.Equals(str2)) {Console.WriteLine(str2);foreach (var ch in str2)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();} }catch (DecoderFallbackException e) {Console.Write("Unable to decode byte(s) ");foreach (byte unknown in e.BytesUnknown)Console.Write("0x{0:X2} ");Console.WriteLine("at index {0}", e.Index);}} } // The example displays the following output: // Ⓢ ⁵ ∞ // 24C8 0020 2075 0020 221E // // Exception: Unable to encode 0x24C8 at index 0.
说明 |
---|
您还可以为编码操作实现自定义异常处理程序。 有关更多信息,请参见部分 Implementing a Custom Fallback Strategy 。 |
EncoderFallbackException 和 DecoderFallbackException 对象提供有关导致异常的条件的信息:
-
EncoderFallbackException 对象包含一个 IsUnknownSurrogate 方法,指示是否字符或无法编码表示未知代理对 (在这种情况下,方法返回 true) 或未知单个字符 (在中,该方法返回 false) 情况下。 该代理项字符对 EncoderFallbackException..::.CharUnknownHigh 和 EncoderFallbackException..::.CharUnknownLow 属性可用。 未知单个字符匹配 EncoderFallbackException..::.CharUnknown 属性可用。 EncoderFallbackException..::.Index 属性指示在第一个字符无法编码的字符串的位置。
-
DecoderFallbackException 对象包括返回无法解码的字节数组一个 BytesUnknown 属性。 DecoderFallbackException..::.Index 属性指示未知字节的起始位置。
虽然 EncoderFallbackException 和 DecoderFallbackException 对象提供有关异常的充足诊断信息,但它们不提供对编码或解码缓冲区。 因此,它们不允许无效数据在编码中替换或更正或解码方法。
除了最佳映射以外通过代码页在内部实现, .NET framework 包括实现回退策略以下类:
-
使用 EncoderReplacementFallback 和 EncoderReplacementFallbackBuffer 来替换编码操作中的字符。
-
使用 DecoderReplacementFallback 和 DecoderReplacementFallbackBuffer 来替换解码操作中的字符。
-
,当字符无法编码的字符时,请使用 EncoderExceptionFallback 和 EncoderExceptionFallbackBuffer 引发 EncoderFallbackException 。
-
,当字符无法解码时,请使用 DecoderExceptionFallback 和 DecoderExceptionFallbackBuffer 引发 DecoderFallbackException 。
此外,您还可以按照以下步骤实现使用最佳回退、替换回退或异常回退的自定义解决方案,例如:
-
派生类从编码操作的 EncoderFallback 和从解码操作的 DecoderFallback 。
-
派生类从编码操作的 EncoderFallbackBuffer 和从解码操作的 DecoderFallbackBuffer 。
-
对于异常回退,因此,如果预定义的 EncoderFallbackException 和 DecoderFallbackException 类无法满足您的需要,可从异常对象派生类 (如 Exception 或 ArgumentException。
从 EncoderFallback 或 DecoderFallback 派生
若要实现自定义回退解决方案,您必须创建从编码操作的 EncoderFallback 继承的类,并将解码操作的 DecoderFallback 。 这些类的实例传递给 Encoding..::.GetEncoding(String, EncoderFallback, DecoderFallback) 方法并在编码类和回退实现之间的中间。
在创建编码器或解码器时自定义回退解决方案,必须实现以下成员:
-
EncoderFallback..::.MaxCharCount 或 DecoderFallback..::.MaxCharCount 属性,该属性返回的最大可能字符数最佳,替换或异常回退可以返回替换单个字符。 对于自定义异常回退,其值为零。
-
EncoderFallback..::.CreateFallbackBuffer 或 DecoderFallback..::.CreateFallbackBuffer 方法,返回您的自定义 EncoderFallbackBuffer 或 DecoderFallbackBuffer 实现。 方法由编码器调用,在解码器遇到第一个字符不能成功进入时,或者,在遇到第一个字节时不能成功解码。
从 EncoderFallbackBuffer 或 DecoderFallbackBuffer 派生
若要实现自定义回退解决方案,还必须创建从编码操作的 EncoderFallbackBuffer 继承的类,并将解码操作的 DecoderFallbackBuffer 。 这些类的实例是由 EncoderFallback 和 DecoderFallback 类的 CreateFallbackBuffer 方法返回。 EncoderFallback..::.CreateFallbackBuffer 方法由编码器调用,在遇到第一个字符时无法进入,并且, DecoderFallback..::.CreateFallbackBuffer 方法由解码器调用,在遇到一个或多个字节时无法解码。 EncoderFallbackBuffer 和 DecoderFallbackBuffer 类提供回退实现。 每个实例表示包含回退字符将替换字符不能输入或字节顺序无法解码的缓冲区。
在创建编码器或解码器时自定义回退解决方案,必须实现以下成员:
-
EncoderFallbackBuffer..::.Fallback 或 DecoderFallbackBuffer..::.Fallback 方法。 EncoderFallbackBuffer..::.Fallback 由编码器调用提供回退缓冲区提供有关它无法编码的字符的信息。 由于要编码的字符可以是代理项对,此方法重载。 一个重载传递给要编码的字符及其在字符串的索引。 第二个重载与其在字符串的索引。通过任何代理。 DecoderFallbackBuffer..::.Fallback 方法由解码器调用提供回退缓冲区提供有关它无法解码的字节的信息。 此方法与第一个字节的索引。通过它无法解码的字节,。 ,如果回退缓冲区可以提供最佳或替换字符,回退方法应返回 true ;否则,它将返回 false。 对于异常回退,回退方法应引发异常。
-
EncoderFallbackBuffer..::.GetNextChar 或 DecoderFallbackBuffer..::.GetNextChar 方法,由编码器或解码器会重复调用从回退缓冲区中的下一个字符。 在返回所有回退字符时,此方法应返回 U+0000。
-
EncoderFallbackBuffer..::.Remaining 或 DecoderFallbackBuffer..::.Remaining 属性,该属性返回回退缓冲区中的剩余字符数。
-
EncoderFallbackBuffer..::.MovePrevious 或 DecoderFallbackBuffer..::.MovePrevious 方法,将回退缓冲区中的当前位置移动到前一个字符。
-
EncoderFallbackBuffer..::.Reset 或 DecoderFallbackBuffer..::.Reset 方法,重新初始化回退缓冲区。
如果回退实现是一个最佳回退或替换回退,从 EncoderFallbackBuffer 和 DecoderFallbackBuffer 派生的类还维护两个私有实例字段:字符的确切的缓冲区的;下一个字符的索引。返回的缓冲区。
EncoderFallback 示例
早期的示例使用替换回退替换没有对应于 ASCII 字符用星号 (Unicode 字符 (*)。 下面的示例使用自定义最佳回退实现来提供非 ASCII 字符的更好映射。
下面的代码定义从派生 EncoderFallback 处理最佳映射非 ASCII 字符 CustomMapper 名为的类。 其 CreateFallbackBuffer 方法返回 CustomMapperFallbackBuffer 对象,提供 EncoderFallbackBuffer 实现。 在一个 64 位整数的两个连续字节中) 的 CustomMapper 类使用一 Dictionary<(Of <(TKey, TValue>)>) 对象存储映射不支持的 Unicode 字符 (键值) 及其相应的 8 位字符 ( 为了使此映射可用于回退缓冲区, CustomMapper 实例作为参数传递给 CustomMapperFallbackBuffer 类构造函数。 因为最长映射是字符串 “INF” Unicode 字符的 U+221E, MaxCharCount 属性返回 3。
Public Class CustomMapper : Inherits EncoderFallbackPublic DefaultString As StringFriend mapping As Dictionary(Of UShort, ULong)Public Sub New()Me.New("?")End SubPublic Sub New(ByVal defaultString As String)Me.DefaultString = defaultString' Create table of mappingsmapping = New Dictionary(Of UShort, ULong)mapping.Add(&H24C8, &H53)mapping.Add(&H2075, &H35)mapping.Add(&H221E, &H49004E0046)End SubPublic Overrides Function CreateFallbackBuffer() As System.Text.EncoderFallbackBufferReturn New CustomMapperFallbackBuffer(Me)End FunctionPublic Overrides ReadOnly Property MaxCharCount As IntegerGetReturn 3End GetEnd Property End Class
public class CustomMapper : EncoderFallback {public string DefaultString;internal Dictionary<ushort, ulong> mapping;public CustomMapper() : this("*"){ }public CustomMapper(string defaultString){this.DefaultString = defaultString;// Create table of mappingsmapping = new Dictionary<ushort, ulong>();mapping.Add(0x24C8, 0x53);mapping.Add(0x2075, 0x35);mapping.Add(0x221E, 0x49004E0046);}public override EncoderFallbackBuffer CreateFallbackBuffer(){return new CustomMapperFallbackBuffer(this);}public override int MaxCharCount{get { return 3; }} }
下面的代码定义 CustomMapperFallbackBuffer 类,从 EncoderFallbackBuffer派生。 包含最佳映射,然后在 CustomMapper 实例中定义的字典来自其类构造函数中可用。 其 Fallback 方法返回 true ,如果 ASCII 编码器无法编码的任何 Unicode 字符。映射字典中定义;否则,它将返回 false。 对于每个回退 count ,专用变量指示仍然要返回的字符数,因此, index 专用变量指示字符串缓冲区, charsToReturn的位置,返回的下一个字符。
Public Class CustomMapperFallbackBuffer : Inherits EncoderFallbackBufferDim count As Integer = -1 ' Number of characters to returnDim index As Integer = -1 ' Index of character to returnDim fb As CustomMapperDim charsToReturn As StringPublic Sub New(ByVal fallback As CustomMapper)MyBase.New()Me.fb = fallbackEnd SubPublic Overloads Overrides Function Fallback(ByVal charUnknownHigh As Char, ByVal charUnknownLow As Char, ByVal index As Integer) As Boolean' Do not try to map surrogates to ASCII.Return FalseEnd FunctionPublic Overloads Overrides Function Fallback(ByVal charUnknown As Char, ByVal index As Integer) As Boolean' Return false if there are already characters to map.If count >= 1 Then Return False' Determine number of characters to return.charsToReturn = String.EmptyDim key As UShort = Convert.ToUInt16(charUnknown)If fb.mapping.ContainsKey(key) ThenDim bytes() As Byte = BitConverter.GetBytes(fb.mapping.Item(key))Dim ctr As IntegerFor Each byt In bytesIf byt > 0 Thenctr += 1charsToReturn += Chr(byt)End IfNextcount = ctrElse' Return default.charsToReturn = fb.DefaultStringcount = 1End IfMe.index = charsToReturn.Length - 1Return TrueEnd FunctionPublic Overrides Function GetNextChar() As Char' We'll return a character if possible, so subtract from the count of chars to return.count -= 1' If count is less than zero, we've returned all characters.If count < 0 Then Return ChrW(0)Me.index -= 1Return charsToReturn(Me.index + 1)End FunctionPublic Overrides Function MovePrevious() As Boolean' Original: if count >= -1 and pos >= 0If count >= -1 Thencount += 1Return TrueElseReturn FalseEnd IfEnd FunctionPublic Overrides ReadOnly Property Remaining As IntegerGetReturn If(count < 0, 0, count)End GetEnd PropertyPublic Overrides Sub Reset()count = -1index = -1End Sub End Class
public class CustomMapperFallbackBuffer : EncoderFallbackBuffer {int count = -1; // Number of characters to returnint index = -1; // Index of character to returnCustomMapper fb; string charsToReturn; public CustomMapperFallbackBuffer(CustomMapper fallback){this.fb = fallback;}public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index){// Do not try to map surrogates to ASCII.return false;}public override bool Fallback(char charUnknown, int index){// Return false if there are already characters to map.if (count >= 1) return false;// Determine number of characters to return.charsToReturn = String.Empty;ushort key = Convert.ToUInt16(charUnknown);if (fb.mapping.ContainsKey(key)) {byte[] bytes = BitConverter.GetBytes(fb.mapping[key]);int ctr = 0;foreach (var byt in bytes) {if (byt > 0) {ctr++;charsToReturn += (char) byt;}}count = ctr;}else {// Return default.charsToReturn = fb.DefaultString;count = 1;}this.index = charsToReturn.Length - 1;return true;}public override char GetNextChar(){// We'll return a character if possible, so subtract from the count of chars to return.count--;// If count is less than zero, we've returned all characters.if (count < 0) return '\u0000';this.index--;return charsToReturn[this.index + 1];}public override bool MovePrevious(){// Original: if count >= -1 and pos >= 0if (count >= -1) {count++;return true;}else {return false;}}public override int Remaining {get { return count < 0 ? 0 : count; }}public override void Reset(){count = -1;index = -1;} }
下面的代码 CustomMapper 然后实例化对象并将其实例。 Encoding..::.GetEncoding(String, EncoderFallback, DecoderFallback) 方法。 该输出指示最佳回退实现成功处理在原始字符串的三个非 ASCII 字符。
Imports System.Text Imports System.Collections.GenericModule Module1Sub Main()Dim enc As Encoding = Encoding.GetEncoding("us-ascii", New CustomMapper(), New DecoderExceptionFallback())Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&H24C8), ChrW(&H2075), ChrW(&H221E))Console.WriteLine(str1)For ctr As Integer = 0 To str1.Length - 1Console.Write("{0} ", Convert.ToUInt16(str1(ctr)).ToString("X4"))If ctr = str1.Length - 1 Then Console.WriteLine()NextConsole.WriteLine()' Encode the original string using the ASCII encoder.Dim bytes() As Byte = enc.GetBytes(str1)Console.Write("Encoded bytes: ")For Each byt In bytesConsole.Write("{0:X2} ", byt)NextConsole.WriteLine()Console.WriteLine()' Decode the ASCII bytes.Dim str2 As String = enc.GetString(bytes)Console.WriteLine("Round-trip: {0}", str1.Equals(str2))If Not str1.Equals(str2) ThenConsole.WriteLine(str2)For Each ch In str2Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))NextConsole.WriteLine()End IfEnd Sub End Module
using System; using System.Collections.Generic; using System.Text;class Program {static void Main(){Encoding enc = Encoding.GetEncoding("us-ascii", new CustomMapper(), new DecoderExceptionFallback());string str1 = "\u24C8 \u2075 \u221E";Console.WriteLine(str1);for (int ctr = 0; ctr <= str1.Length - 1; ctr++) {Console.Write("{0} ", Convert.ToUInt16(str1[ctr]).ToString("X4"));if (ctr == str1.Length - 1) Console.WriteLine();}Console.WriteLine();// Encode the original string using the ASCII encoder.byte[] bytes = enc.GetBytes(str1);Console.Write("Encoded bytes: ");foreach (var byt in bytes)Console.Write("{0:X2} ", byt);Console.WriteLine("\n");// Decode the ASCII bytes.string str2 = enc.GetString(bytes);Console.WriteLine("Round-trip: {0}", str1.Equals(str2));if (! str1.Equals(str2)) {Console.WriteLine(str2);foreach (var ch in str2)Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));Console.WriteLine();}} }