Como usar classes de codificação de caracteres no .NET
Este artigo explica como usar as classes que o .NET fornece para codificação e decodificação de texto usando vários esquemas de codificação. As instruções pressupõem que você leu Introdução à codificação de caracteres no .NET.
Codificadores e descodificadores
O .NET fornece classes de codificação que codificam e decodificam texto usando vários sistemas de codificação. Por exemplo, a UTF8Encoding classe descreve as regras para codificação e decodificação de UTF-8. O .NET usa codificação UTF-16 (representada pela UnicodeEncoding classe) para string
instâncias. Codificadores e decodificadores estão disponíveis para outros esquemas de codificação.
A codificação e a descodificação também podem incluir validação. Por exemplo, a UnicodeEncoding classe verifica todas as char
instâncias no intervalo substituto para certificar-se de que estão em pares substitutos válidos. Uma estratégia de fallback determina como um codificador lida com caracteres inválidos ou como um decodificador lida com bytes inválidos.
Aviso
As classes de codificação .NET fornecem uma maneira de armazenar e converter dados de caracteres. Eles não devem ser usados para armazenar dados binários em forma de cadeia de caracteres. Dependendo da codificação usada, a conversão de dados binários para o formato de cadeia de caracteres com as classes de codificação pode introduzir um comportamento inesperado e produzir dados imprecisos ou corrompidos. Para converter dados binários em um formulário de cadeia de caracteres, use o Convert.ToBase64String método.
Todas as classes de codificação de caracteres no .NET herdam da System.Text.Encoding classe, que é uma classe abstrata que define a funcionalidade comum a todas as codificações de caracteres. Para acessar os objetos de codificação individuais implementados no .NET, faça o seguinte:
Use as propriedades estáticas da Encoding classe, que retornam objetos que representam as codificações de caracteres padrão disponíveis no .NET (ASCII, UTF-7, UTF-8, UTF-16 e UTF-32). Por exemplo, a Encoding.Unicode propriedade retorna um UnicodeEncoding objeto. Cada objeto usa fallback de substituição para manipular cadeias de caracteres que não pode codificar e bytes que não pode decodificar. Para obter mais informações, consulte Fallback de substituição.
Chame o construtor de classe da codificação. Os objetos para as codificações ASCII, UTF-7, UTF-8, UTF-16 e UTF-32 podem ser instanciados dessa maneira. Por padrão, cada objeto usa fallback de substituição para manipular cadeias de caracteres que não pode codificar e bytes que não pode decodificar, mas você pode especificar que uma exceção deve ser lançada. Para obter mais informações, consulte Fallback de substituição e Fallback de exceção.
Chame o Encoding(Int32) construtor e passe-lhe um inteiro que representa a codificação. Os objetos de codificação padrão usam fallback de substituição, e os objetos de codificação DBCS (code page e double-byte character set) usam fallback de melhor ajuste para manipular cadeias de caracteres que não podem codificar e bytes que não podem decodificar. Para obter mais informações, consulte Fallback de melhor ajuste.
Chame o Encoding.GetEncoding método, que retorna qualquer padrão, página de código ou codificação DBCS disponível no .NET. As sobrecargas permitem especificar um objeto de fallback para o codificador e o decodificador.
Você pode recuperar informações sobre todas as codificações disponíveis no .NET chamando o Encoding.GetEncodings método. O .NET suporta os esquemas de codificação de caracteres listados na tabela a seguir.
Classe de codificação | Description |
---|---|
ASCII | Codifica um intervalo limitado de caracteres usando os sete bits inferiores de um byte. Como essa codificação só suporta valores de caracteres de U+0000 até , U+007F na maioria dos casos ela é inadequada para aplicativos internacionalizados. |
UTF-7 | Representa caracteres como sequências de caracteres ASCII de 7 bits. Caracteres Unicode não-ASCII são representados por uma sequência de escape de caracteres ASCII. UTF-7 suporta protocolos como e-mail e newsgroup. No entanto, o UTF-7 não é particularmente seguro ou robusto. Em alguns casos, alterar um bit pode alterar radicalmente a interpretação de uma string UTF-7 inteira. Em outros casos, diferentes cadeias de caracteres UTF-7 podem codificar o mesmo texto. Para sequências que incluem caracteres não-ASCII, UTF-7 requer mais espaço do que UTF-8, e a codificação/decodificação é mais lenta. Consequentemente, você deve usar UTF-8 em vez de UTF-7, se possível. |
UTF-8 | Representa cada ponto de código Unicode como uma sequência de um a quatro bytes. UTF-8 suporta tamanhos de dados de 8 bits e funciona bem com muitos sistemas operacionais existentes. Para o intervalo de caracteres ASCII, UTF-8 é idêntico à codificação ASCII e permite um conjunto mais amplo de caracteres. No entanto, para scripts chinês-japonês-coreano (CJK), UTF-8 pode exigir três bytes para cada caractere e pode causar tamanhos de dados maiores do que UTF-16. Às vezes, a quantidade de dados ASCII, como tags HTML, justifica o aumento do tamanho para o intervalo CJK. |
UTF-16 | Representa cada ponto de código Unicode como uma sequência de um ou dois inteiros de 16 bits. Os caracteres Unicode mais comuns requerem apenas um ponto de código UTF-16, embora os caracteres suplementares Unicode (U+10000 e superior) exijam dois pontos de código substitutos UTF-16. As ordens de bytes little-endian e big-endian são suportadas. A codificação UTF-16 é usada pelo Common Language Runtime para representar Char e String valores, e é usada pelo sistema operacional Windows para representar WCHAR valores. |
UTF-32 | Representa cada ponto de código Unicode como um inteiro de 32 bits. As ordens de bytes little-endian e big-endian são suportadas. A codificação UTF-32 é usada quando os aplicativos querem evitar o comportamento de ponto de código substituto da codificação UTF-16 em sistemas operacionais para os quais o espaço codificado é muito importante. Glifos únicos renderizados em um monitor ainda podem ser codificados com mais de um caractere UTF-32. |
Codificação ANSI/ISO | Fornece suporte para uma variedade de páginas de código. Em sistemas operacionais Windows, as páginas de código são usadas para oferecer suporte a um idioma específico ou grupo de idiomas. Para obter uma tabela que lista as páginas de código suportadas pelo .NET, consulte a Encoding classe. Você pode recuperar um objeto de codificação para uma página de código específica chamando o Encoding.GetEncoding(Int32) método. Uma página de código contém 256 pontos de código e é baseada em zero. Na maioria das páginas de código, os pontos de código 0 a 127 representam o conjunto de caracteres ASCII e os pontos de código 128 a 255 diferem significativamente entre as páginas de código. Por exemplo, a página de código 1252 fornece os caracteres para sistemas de escrita latinos, incluindo inglês, alemão e francês. Os últimos 128 pontos de código na página de código 1252 contêm os caracteres acentuados. A página de código 1253 fornece códigos de caracteres que são necessários no sistema de escrita grego. Os últimos 128 pontos de código na página de código 1253 contêm os caracteres gregos. Como resultado, um aplicativo que depende de páginas de código ANSI não pode armazenar grego e alemão no mesmo fluxo de texto, a menos que inclua um identificador que indique a página de código referenciada. |
Codificações DBCS (conjunto de caracteres de byte duplo) | Suporta idiomas, como chinês, japonês e coreano, que contêm mais de 256 caracteres. Em um DBCS, um par de pontos de código (um byte duplo) representa cada caractere. A Encoding.IsSingleByte propriedade retorna false para codificações DBCS. Você pode recuperar um objeto de codificação para um DBCS específico chamando o Encoding.GetEncoding(Int32) método. Quando um aplicativo manipula dados DBCS, o primeiro byte de um caractere DBCS (o byte principal) é processado em combinação com o byte de trilha que imediatamente o segue. Como um único par de pontos de código de byte duplo pode representar caracteres diferentes dependendo da página de código, esse esquema ainda não permite a combinação de dois idiomas, como japonês e chinês, no mesmo fluxo de dados. |
Essas codificações permitem que você trabalhe com caracteres Unicode, bem como com codificações que são mais comumente usadas em aplicativos herdados. Além disso, você pode criar uma codificação personalizada definindo uma classe que deriva e Encoding substitui seus membros.
Suporte à codificação .NET Core
Por padrão, o .NET Core não disponibiliza nenhuma codificação de página de código além da página de código 28591 e as codificações Unicode, como UTF-8 e UTF-16. No entanto, você pode adicionar as codificações de página de código encontradas em aplicativos padrão do Windows destinados ao .NET ao seu aplicativo. Para obter mais informações, consulte o CodePagesEncodingProvider tópico .
Selecionando uma classe de codificação
Se você tiver a oportunidade de escolher a codificação a ser usada pelo seu aplicativo, você deve usar uma codificação Unicode, de preferência ou UTF8EncodingUnicodeEncoding. (O .NET também suporta uma terceira codificação Unicode, UTF32Encoding.)
Se você estiver planejando usar uma codificação ASCII (ASCIIEncoding), escolha UTF8Encoding em vez disso. As duas codificações são idênticas para o conjunto de caracteres ASCII, mas UTF8Encoding tem as seguintes vantagens:
Ele pode representar todos os caracteres Unicode, enquanto ASCIIEncoding suporta apenas os valores de caracteres Unicode entre U+0000 e U+007F.
Ele fornece deteção de erros e melhor segurança.
Foi ajustado para ser o mais rápido possível e deve ser mais rápido do que qualquer outra codificação. Mesmo para conteúdo totalmente ASCII, as operações realizadas com UTF8Encoding são mais rápidas do que as operações executadas com ASCIIEncodingo .
Você deve considerar o uso ASCIIEncoding apenas para aplicativos herdados. No entanto, mesmo para aplicativos herdados, UTF8Encoding pode ser uma escolha melhor pelos seguintes motivos (assumindo as configurações padrão):
Se o seu aplicativo tiver conteúdo que não é estritamente ASCII e codificá-lo com ASCIIEncoding, cada caractere não-ASCII codifica como um ponto de interrogação (?). Se o aplicativo decodificar esses dados, as informações serão perdidas.
Se o seu aplicativo tiver conteúdo que não é estritamente ASCII e codificá-lo com UTF8Encoding, o resultado parece ininteligível se interpretado como ASCII. No entanto, se o aplicativo usar um decodificador UTF-8 para decodificar esses dados, os dados executarão uma viagem de ida e volta com êxito.
Em um aplicativo Web, os caracteres enviados ao cliente em resposta a uma solicitação da Web devem refletir a codificação usada no cliente. Na maioria dos casos, você deve definir a HttpResponse.ContentEncoding propriedade como o valor retornado pela HttpRequest.ContentEncoding propriedade para exibir o texto na codificação esperada pelo usuário.
Usando um objeto de codificação
Um codificador converte uma cadeia de caracteres (mais comumente, caracteres Unicode) em seu equivalente numérico (byte). Por exemplo, você pode usar um codificador ASCII para converter caracteres Unicode em ASCII para que eles possam ser exibidos no console. Para executar a conversão, você chama o Encoding.GetBytes método. Se você quiser determinar quantos bytes são necessários para armazenar os caracteres codificados antes de executar a codificação, você pode chamar o GetByteCount método.
O exemplo a seguir usa uma única matriz de bytes para codificar cadeias de caracteres em duas operações separadas. Ele mantém um índice que indica a posição inicial na matriz de bytes para o próximo conjunto de bytes codificados em ASCII. Ele chama o ASCIIEncoding.GetByteCount(String) método para garantir que a matriz de bytes seja grande o suficiente para acomodar a cadeia de caracteres codificada. Em seguida, ele chama o ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) método para codificar os caracteres na cadeia de caracteres.
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.
Imports System.Text
Module Example
Public 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 = 0
Console.WriteLine("Strings to encode:")
For Each stringValue In strings
Console.WriteLine(" {0}", stringValue)
Dim count As Integer = asciiEncoding.GetByteCount(stringValue)
If count + index >= bytes.Length Then
Array.Resize(bytes, bytes.Length + 50)
End If
Dim 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 Sub
Private Function ShowByteValues(bytes As Byte(), last As Integer) As String
Dim returnString As String = " "
For ctr As Integer = 0 To last - 1
If ctr Mod 20 = 0 Then returnString += vbCrLf + " "
returnString += String.Format("{0:X2} ", bytes(ctr))
Next
Return returnString
End 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.
Um decodificador converte uma matriz de bytes que reflete uma codificação de caracteres específica em um conjunto de caracteres, seja em uma matriz de caracteres ou em uma cadeia de caracteres. Para decodificar uma matriz de bytes em uma matriz de caracteres, chame o Encoding.GetChars método. Para decodificar uma matriz de bytes em uma cadeia de caracteres, chame o GetString método. Se você quiser determinar quantos caracteres são necessários para armazenar os bytes decodificados antes de executar a decodificação, você pode chamar o GetCharCount método.
O exemplo a seguir codifica três cadeias de caracteres e, em seguida, as decodifica em uma única matriz de caracteres. Ele mantém um índice que indica a posição inicial na matriz de caracteres para o próximo conjunto de caracteres decodificados. Ele chama o GetCharCount método para garantir que a matriz de caracteres seja grande o suficiente para acomodar todos os caracteres decodificados. Em seguida, ele chama o ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) método para decodificar a matriz de bytes.
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.
Imports System.Text
Module Example
Public 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 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 ctr As Integer = 0 To bytes.Length - 1
Console.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 Then
Array.Resize(chars, chars.Length + 50)
End If
Dim written As Integer = asciiEncoding.GetChars(bytes, 0,
bytes.Length,
chars, index)
index = index + written
Console.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.
Os métodos de codificação e decodificação de uma classe derivada são projetados para trabalhar em um conjunto completo de dados, ou seja, todos os dados a serem codificados ou decodificados são fornecidos em uma única chamada de Encoding método. No entanto, em alguns casos, os dados estão disponíveis em um fluxo, e os dados a serem codificados ou decodificados podem estar disponíveis apenas a partir de operações de leitura separadas. Isso requer que a operação de codificação ou decodificação se lembre de qualquer estado salvo de sua chamada anterior. Métodos de classes derivadas de e Decoder são capazes de lidar com operações de codificação e decodificação que abrangem várias chamadas de Encoder método.
Um Encoder objeto para uma codificação específica está disponível na propriedade dessa Encoding.GetEncoder codificação. Um Decoder objeto para uma codificação específica está disponível na propriedade dessa Encoding.GetDecoder codificação. Para operações de decodificação, observe que as classes derivadas de Decoder incluem um Decoder.GetChars método, mas não têm um método que corresponda a Encoding.GetString.
O exemplo a seguir ilustra a diferença entre usar os Encoding.GetString métodos e Decoder.GetChars para decodificar uma matriz de bytes Unicode. O exemplo codifica uma cadeia de caracteres que contém alguns caracteres Unicode para um arquivo e, em seguida, usa os dois métodos de decodificação para decodificá-los dez bytes de cada vez. Como um par substituto ocorre no décimo e décimo primeiro bytes, ele é decodificado em chamadas de método separadas. Como mostra a saída, o Encoding.GetString método não é capaz de decodificar corretamente os bytes e, em vez disso, os substitui por U + FFFD (CARACTERE DE SUBSTITUIÇÃO). Por outro lado, o Decoder.GetChars método é capaz de decodificar com êxito a matriz de bytes para obter a cadeia de caracteres original.
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
Imports System.IO
Imports System.Text
Module Example
Public 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 = 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)
Dim decoder As Decoder = enc.GetDecoder()
Dim chars(50) As Char
Dim 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 = count
br.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 Sub
Private 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 Then
Console.WriteLine("Code points in original string:")
For Each ch In original
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine("Code points in decoded string:")
For Each ch In decoded
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End 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
Escolhendo uma estratégia de fallback
Quando um método tenta codificar ou decodificar um caractere, mas não existe mapeamento, ele deve implementar uma estratégia de fallback que determine como o mapeamento com falha deve ser manipulado. Existem três tipos de estratégias de fallback:
Fallback mais adequado
Fallback de substituição
Fallback de exceção
Importante
Os problemas mais comuns em operações de codificação ocorrem quando um caractere Unicode não pode ser mapeado para uma codificação de página de código específica. Os problemas mais comuns em operações de decodificação ocorrem quando sequências de bytes inválidas não podem ser traduzidas em caracteres Unicode válidos. Por esses motivos, você deve saber qual estratégia de fallback um determinado objeto de codificação usa. Sempre que possível, você deve especificar a estratégia de fallback usada por um objeto de codificação ao instanciar o objeto.
Fallback mais adequado
Quando um caractere não tem uma correspondência exata na codificação de destino, o codificador pode tentar mapeá-lo para um caractere semelhante. (O fallback mais adequado é principalmente um problema de codificação e não de decodificação. Há muito poucas páginas de código que contêm caracteres que não podem ser mapeados com êxito para Unicode.) O fallback mais adequado é o padrão para codificações de página de código e conjunto de caracteres de byte duplo que são recuperadas pelas sobrecargas e Encoding.GetEncoding(Int32)Encoding.GetEncoding(String) .
Nota
Em teoria, as classes de codificação Unicode fornecidas no .NET (UTF8Encoding, UnicodeEncoding, e UTF32Encoding) suportam todos os caracteres em cada conjunto de caracteres, para que possam ser usadas para eliminar problemas de fallback mais adequados.
As estratégias mais adequadas variam para diferentes páginas de código. Por exemplo, para algumas páginas de código, os caracteres latinos de largura total são mapeados para os caracteres latinos de meia largura mais comuns. Para outras páginas de código, esse mapeamento não é feito. Mesmo sob uma estratégia agressiva de melhor ajuste, não há ajuste imaginável para alguns personagens em algumas codificações. Por exemplo, um ideógrafo chinês não tem um mapeamento razoável para a página de código 1252. Nesse caso, uma cadeia de caracteres de substituição é usada. Por padrão, essa cadeia de caracteres é apenas um único PONTO DE INTERROGAÇÃO (U+003F).
Nota
As estratégias mais adequadas não estão documentadas em pormenor. No entanto, várias páginas de código estão documentadas no site do Unicode Consortium. Por favor, revise o arquivo de readme.txt nessa pasta para obter uma descrição de como interpretar os arquivos de mapeamento.
O exemplo a seguir usa a página de código 1252 (a página de código do Windows para idiomas da Europa Ocidental) para ilustrar o mapeamento mais adequado e suas desvantagens. O Encoding.GetEncoding(Int32) método é usado para recuperar um objeto de codificação para a página de código 1252. Por padrão, ele usa um mapeamento de melhor ajuste para caracteres Unicode que não suporta. O exemplo instancia uma cadeia de caracteres que contém três caracteres não-ASCII - LETRAS MAIÚSCULAS LATINAS CIRCULARES S (U+24C8), CINCO SOBRESCRITOS (U+2075) e INFINITY (U+221E) - separados por espaços. Como mostra a saída do exemplo, quando a cadeia de caracteres é codificada, os três caracteres não espaciais originais são substituídos por PONTO DE INTERROGAÇÃO (U+003F), DÍGITO CINCO (U+0035) e DÍGITO OITO (U+0038). DIGIT EIGHT é um substituto particularmente pobre para o caractere INFINITY não suportado, e PONTO DE INTERROGAÇÃO indica que nenhum mapeamento estava disponível para o personagem original.
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
Imports System.Text
Module Example
Public 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 str
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine()
' Encode a Unicode string.
Dim bytes() As Byte = cp1252.GetBytes(str)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.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) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
End If
End 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
O mapeamento de melhor ajuste é o comportamento padrão para um Encoding objeto que codifica dados Unicode em dados de página de código, e há aplicativos herdados que dependem desse comportamento. No entanto, a maioria dos novos aplicativos deve evitar o comportamento mais adequado por motivos de segurança. Por exemplo, os aplicativos não devem colocar um nome de domínio por meio de uma codificação mais adequada.
Nota
Você também pode implementar um mapeamento de fallback personalizado de melhor ajuste para uma codificação. Para obter mais informações, consulte a seção Implementando uma estratégia de fallback personalizada.
Se o fallback de melhor ajuste for o padrão para um objeto de codificação, você poderá escolher outra estratégia de fallback ao recuperar um Encoding objeto chamando a Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) sobrecarga ou Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) . A seção a seguir inclui um exemplo que substitui cada caractere que não pode ser mapeado para a página de código 1252 por um asterisco (*).
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
Imports System.Text
Module Example
Public 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 str1
Console.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) Then
Console.WriteLine(str2)
For Each ch In str2
Console.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
Fallback de substituição
Quando um caractere não tem uma correspondência exata no esquema de destino, mas não há nenhum caractere apropriado para o qual ele possa ser mapeado, o aplicativo pode especificar um caractere ou cadeia de caracteres de substituição. Este é o comportamento padrão para o decodificador Unicode, que substitui qualquer sequência de dois bytes que não pode decodificar com REPLACEMENT_CHARACTER (U + FFFD). É também o ASCIIEncoding comportamento padrão da classe, que substitui cada caractere que não pode codificar ou decodificar por um ponto de interrogação. O exemplo a seguir ilustra a substituição de caracteres para a cadeia de caracteres Unicode do exemplo anterior. Como mostra a saída, cada caractere que não pode ser decodificado em um valor de byte ASCII é substituído por 0x3F, que é o código ASCII para um ponto de interrogação.
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
Imports System.Text
Module Example
Public Sub Main()
Dim enc As Encoding = Encoding.Ascii
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.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 bytes
Console.Write("{0:X2} ", byt)
Next
Console.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) Then
Console.WriteLine(str2)
For Each ch In str2
Console.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
O .NET inclui as classes andDecoderReplacementFallback, que substituem uma cadeia de caracteres de substituição se um caractere não for mapeado EncoderReplacementFallback exatamente em uma operação de codificação ou decodificação. Por padrão, essa cadeia de caracteres de substituição é um ponto de interrogação, mas você pode chamar uma sobrecarga de construtor de classe para escolher uma cadeia de caracteres diferente. Normalmente, a cadeia de caracteres de substituição é um único caractere, embora isso não seja um requisito. O exemplo a seguir altera o comportamento do codificador de página de código 1252 instanciando um EncoderReplacementFallback objeto que usa um asterisco (*) como uma cadeia de caracteres de substituição.
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
Imports System.Text
Module Example
Public 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 str1
Console.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) Then
Console.WriteLine(str2)
For Each ch In str2
Console.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
Nota
Você também pode implementar uma classe de substituição para uma codificação. Para obter mais informações, consulte a seção Implementando uma estratégia de fallback personalizada.
Além de PONTO DE INTERROGAÇÃO (U+003F), o caractere de substituição Unicode (U+FFFD) é comumente usado como uma cadeia de caracteres de substituição, particularmente ao decodificar sequências de bytes que não podem ser traduzidas com êxito em caracteres Unicode. No entanto, você é livre para escolher qualquer cadeia de caracteres de substituição, e ela pode conter vários caracteres.
Fallback de exceção
Em vez de fornecer um fallback mais adequado ou uma cadeia de caracteres de substituição, um codificador pode lançar um EncoderFallbackException se não for capaz de codificar um conjunto de caracteres, e um decodificador pode lançar um DecoderFallbackException se não for capaz de decodificar uma matriz de bytes. Para lançar uma exceção em operações de codificação e decodificação, forneça um EncoderExceptionFallback objeto e um DecoderExceptionFallback objeto, respectivamente, para o Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) método. O exemplo a seguir ilustra o fallback de exceção com a ASCIIEncoding classe.
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);
else
Console.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.
Imports System.Text
Module Example
Public 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 str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine()
' Encode the original string using the ASCII encoder.
Dim bytes() As Byte = {}
Try
bytes = enc.GetBytes(str1)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.WriteLine()
Catch e As EncoderFallbackException
Console.Write("Exception: ")
If e.IsUnknownSurrogate() Then
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)
Else
Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.",
Convert.ToUInt16(e.CharUnknown),
e.Index)
End If
Exit Sub
End Try
Console.WriteLine()
' Decode the ASCII bytes.
Try
Dim str2 As String = enc.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
Catch e As DecoderFallbackException
Console.Write("Unable to decode byte(s) ")
For Each unknown As Byte In e.BytesUnknown
Console.Write("0x{0:X2} ")
Next
Console.WriteLine("at index {0}", e.Index)
End Try
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
'
' Exception: Unable to encode 0x24C8 at index 0.
Nota
Você também pode implementar um manipulador de exceção personalizado para uma operação de codificação. Para obter mais informações, consulte a seção Implementando uma estratégia de fallback personalizada.
Os EncoderFallbackException objetos e DecoderFallbackException fornecem as seguintes informações sobre a condição que causou a exceção:
O EncoderFallbackException objeto inclui um IsUnknownSurrogate método, que indica se o caractere ou caracteres que não podem ser codificados representam um par substituto desconhecido (nesse caso, o método retorna
true
) ou um único caractere desconhecido (nesse caso, o método retornafalse
). Os caracteres no par substituto estão disponíveis nas EncoderFallbackException.CharUnknownHigh propriedades e EncoderFallbackException.CharUnknownLow . O caractere único desconhecido está disponível na EncoderFallbackException.CharUnknown propriedade. A EncoderFallbackException.Index propriedade indica a posição na cadeia de caracteres na qual o primeiro caractere que não pôde ser codificado foi encontrado.O DecoderFallbackException objeto inclui uma BytesUnknown propriedade que retorna uma matriz de bytes que não podem ser decodificados. A DecoderFallbackException.Index propriedade indica a posição inicial dos bytes desconhecidos.
Embora os EncoderFallbackException objetos e DecoderFallbackException forneçam informações de diagnóstico adequadas sobre a exceção, eles não fornecem acesso ao buffer de codificação ou decodificação. Portanto, eles não permitem que dados inválidos sejam substituídos ou corrigidos dentro do método de codificação ou decodificação.
Implementando uma estratégia de fallback personalizada
Além do mapeamento mais adequado que é implementado internamente por páginas de código, o .NET inclui as seguintes classes para implementar uma estratégia de fallback:
Use EncoderReplacementFallback e EncoderReplacementFallbackBuffer substitua caracteres em operações de codificação.
Use DecoderReplacementFallback e DecoderReplacementFallbackBuffer substitua caracteres em operações de decodificação.
Use EncoderExceptionFallback e lance um EncoderFallbackException quando um caractere EncoderExceptionFallbackBuffer não pode ser codificado.
Use DecoderExceptionFallback e lance um DecoderFallbackException quando um caractere DecoderExceptionFallbackBuffer não pode ser decodificado.
Além disso, você pode implementar uma solução personalizada que usa fallback de melhor ajuste, fallback de substituição ou fallback de exceção, seguindo estas etapas:
Derive uma classe de para operações de codificação e de para operações de EncoderFallbackDecoderFallback decodificação.
Derive uma classe de para operações de codificação e de para operações de EncoderFallbackBufferDecoderFallbackBuffer decodificação.
Para fallback de exceção, se as classes e DecoderFallbackException predefinidas EncoderFallbackException não atenderem às suas necessidades, derive uma classe de um objeto de exceção como Exception ou ArgumentException.
Derivando de EncoderFallback ou DecoderFallback
Para implementar uma solução de fallback personalizada, você deve criar uma classe que herda de para operações de EncoderFallback codificação e de para operações de DecoderFallback decodificação. Instâncias dessas classes são passadas para o Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) método e servem como intermediário entre a classe de codificação e a implementação de fallback.
Ao criar uma solução de fallback personalizada para um codificador ou decodificador, você deve implementar os seguintes membros:
A EncoderFallback.MaxCharCount propriedade or DecoderFallback.MaxCharCount , que retorna o número máximo possível de caracteres que o fallback de melhor ajuste, substituição ou exceção pode retornar para substituir um único caractere. Para um fallback de exceção personalizada, seu valor é zero.
O EncoderFallback.CreateFallbackBuffer método or DecoderFallback.CreateFallbackBuffer , que retorna seu personalizado EncoderFallbackBuffer ou DecoderFallbackBuffer implementação. O método é chamado pelo codificador quando encontra o primeiro caractere que não consegue codificar com êxito, ou pelo decodificador quando encontra o primeiro byte que não consegue decodificar com êxito.
Derivando de EncoderFallbackBuffer ou DecoderFallbackBuffer
Para implementar uma solução de fallback personalizada, você também deve criar uma classe que herda de para operações de EncoderFallbackBuffer codificação e de para operações de DecoderFallbackBuffer decodificação. Instâncias dessas classes são retornadas pelo CreateFallbackBuffer método das EncoderFallback classes and DecoderFallback . O EncoderFallback.CreateFallbackBuffer método é chamado pelo codificador quando encontra o primeiro caractere que não é capaz de codificar, e o DecoderFallback.CreateFallbackBuffer método é chamado pelo decodificador quando encontra um ou mais bytes que não é capaz de decodificar. As EncoderFallbackBuffer classes e DecoderFallbackBuffer fornecem a implementação de fallback. Cada instância representa um buffer que contém os caracteres de fallback que substituirão o caractere que não pode ser codificado ou a sequência de bytes que não pode ser decodificada.
Ao criar uma solução de fallback personalizada para um codificador ou decodificador, você deve implementar os seguintes membros:
O EncoderFallbackBuffer.Fallback ou DecoderFallbackBuffer.Fallback método. EncoderFallbackBuffer.Fallback é chamado pelo codificador para fornecer ao buffer de fallback informações sobre o caractere que ele não pode codificar. Como o caractere a ser codificado pode ser um par substituto, esse método está sobrecarregado. Uma sobrecarga é passada o caractere a ser codificado e seu índice na cadeia de caracteres. A segunda sobrecarga é passada o substituto alto e baixo junto com seu índice na string. O DecoderFallbackBuffer.Fallback método é chamado pelo decodificador para fornecer ao buffer de fallback informações sobre os bytes que ele não pode decodificar. Este método é passado uma matriz de bytes que ele não pode decodificar, juntamente com o índice do primeiro byte. O método de fallback deve retornar
true
se o buffer de fallback puder fornecer um caractere ou caracteres de melhor ajuste ou substituição, caso contrário, ele deve retornarfalse
. Para um fallback de exceção, o método de fallback deve lançar uma exceção.O EncoderFallbackBuffer.GetNextChar método or DecoderFallbackBuffer.GetNextChar , que é chamado repetidamente pelo codificador ou decodificador para obter o próximo caractere do buffer de fallback. Quando todos os caracteres de fallback tiverem sido retornados, o método deverá retornar U+0000.
A EncoderFallbackBuffer.Remaining propriedade or DecoderFallbackBuffer.Remaining , que retorna o número de caracteres restantes no buffer de fallback.
O EncoderFallbackBuffer.MovePrevious método or DecoderFallbackBuffer.MovePrevious , que move a posição atual no buffer de fallback para o caractere anterior.
O EncoderFallbackBuffer.Reset método or DecoderFallbackBuffer.Reset , que reinicializa o buffer de fallback.
Se a implementação de fallback for um fallback de melhor ajuste ou um fallback de substituição, as classes derivaram e DecoderFallbackBuffer também manterão dois campos de EncoderFallbackBuffer instância privada: o número exato de caracteres no buffer e o índice do próximo caractere no buffer a ser retornado.
Um exemplo de EncoderFallback
Um exemplo anterior usou fallback de substituição para substituir caracteres Unicode que não correspondiam a caracteres ASCII por um asterisco (*). O exemplo a seguir usa uma implementação de fallback personalizada de melhor ajuste para fornecer um mapeamento melhor de caracteres não-ASCII.
O código a seguir define uma classe nomeada CustomMapper
que é derivada para EncoderFallback manipular o mapeamento mais adequado de caracteres não-ASCII. Seu CreateFallbackBuffer
método retorna um CustomMapperFallbackBuffer
objeto, que fornece a EncoderFallbackBuffer implementação. A CustomMapper
classe usa um Dictionary<TKey,TValue> objeto para armazenar os mapeamentos de caracteres Unicode sem suporte (o valor da chave) e seus caracteres correspondentes de 8 bits (que são armazenados em dois bytes consecutivos em um inteiro de 64 bits). Para disponibilizar esse mapeamento para o buffer de fallback, a CustomMapper
instância é passada como um parâmetro para o CustomMapperFallbackBuffer
construtor de classe. Como o mapeamento mais longo é a cadeia de caracteres "INF" para o caractere Unicode U+221E, a MaxCharCount
propriedade retorna 3.
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 mappings
mapping = 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; }
}
}
Public Class CustomMapper : Inherits EncoderFallback
Public DefaultString As String
Friend mapping As Dictionary(Of UShort, ULong)
Public Sub New()
Me.New("?")
End Sub
Public Sub New(ByVal defaultString As String)
Me.DefaultString = defaultString
' Create table of mappings
mapping = New Dictionary(Of UShort, ULong)
mapping.Add(&H24C8, &H53)
mapping.Add(&H2075, &H35)
mapping.Add(&H221E, &H49004E0046)
End Sub
Public Overrides Function CreateFallbackBuffer() As System.Text.EncoderFallbackBuffer
Return New CustomMapperFallbackBuffer(Me)
End Function
Public Overrides ReadOnly Property MaxCharCount As Integer
Get
Return 3
End Get
End Property
End Class
O código a seguir define a CustomMapperFallbackBuffer
classe, que é derivada de EncoderFallbackBuffer. O dicionário que contém mapeamentos de melhor ajuste e que é definido na instância está disponível em CustomMapper
seu construtor de classe. Seu Fallback
método retorna true
se algum dos caracteres Unicode que o codificador ASCII não pode codificar estiver definido no dicionário de mapeamento, caso contrário, ele retornará false
. Para cada fallback, a variável private count
indica o número de caracteres que ainda precisam ser retornados e a variável private index
indica a posição no buffer de cadeia de caracteres, charsToReturn
, do próximo caractere a ser retornado.
public class CustomMapperFallbackBuffer : EncoderFallbackBuffer
{
int count = -1; // Number of characters to return
int index = -1; // Index of character to return
CustomMapper 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 >= 0
if (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;
}
}
Public Class CustomMapperFallbackBuffer : Inherits EncoderFallbackBuffer
Dim count As Integer = -1 ' Number of characters to return
Dim index As Integer = -1 ' Index of character to return
Dim fb As CustomMapper
Dim charsToReturn As String
Public Sub New(ByVal fallback As CustomMapper)
MyBase.New()
Me.fb = fallback
End Sub
Public 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 False
End Function
Public 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.Empty
Dim key As UShort = Convert.ToUInt16(charUnknown)
If fb.mapping.ContainsKey(key) Then
Dim bytes() As Byte = BitConverter.GetBytes(fb.mapping.Item(key))
Dim ctr As Integer
For Each byt In bytes
If byt > 0 Then
ctr += 1
charsToReturn += Chr(byt)
End If
Next
count = ctr
Else
' Return default.
charsToReturn = fb.DefaultString
count = 1
End If
Me.index = charsToReturn.Length - 1
Return True
End Function
Public 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 -= 1
Return charsToReturn(Me.index + 1)
End Function
Public Overrides Function MovePrevious() As Boolean
' Original: if count >= -1 and pos >= 0
If count >= -1 Then
count += 1
Return True
Else
Return False
End If
End Function
Public Overrides ReadOnly Property Remaining As Integer
Get
Return If(count < 0, 0, count)
End Get
End Property
Public Overrides Sub Reset()
count = -1
index = -1
End Sub
End Class
O código a seguir instancia o CustomMapper
objeto e passa uma instância dele para o Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) método. A saída indica que a implementação de fallback mais adequada lida com êxito com os três caracteres não-ASCII na cadeia de caracteres original.
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();
}
}
}
Imports System.Text
Imports System.Collections.Generic
Module Module1
Sub 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 - 1
Console.Write("{0} ", Convert.ToUInt16(str1(ctr)).ToString("X4"))
If ctr = str1.Length - 1 Then Console.WriteLine()
Next
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 bytes
Console.Write("{0:X2} ", byt)
Next
Console.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) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module