Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo illustra come usare le classi fornite da .NET per la codifica e la decodifica del testo usando vari schemi di codifica. Le istruzioni presuppongono di aver letto Introduzione alla codifica dei caratteri in .NET.
Codificatori e decodificatori
.NET fornisce classi di codifica che codificano e decodificano il testo usando vari sistemi di codifica. Ad esempio, la classe UTF8Encoding descrive le regole per la codifica e la decodifica da UTF-8. .NET usa la codifica UTF-16 (rappresentata dalla classe UnicodeEncoding) per le istanze di string. I codificatori e i decodificatori sono disponibili per altri schemi di codifica.
La codifica e la decodifica possono includere anche la convalida. Ad esempio, la classe UnicodeEncoding controlla tutte le istanze char nell'intervallo di surrogati per assicurarsi che si trovino in coppie surrogate valide. Una strategia di fallback determina il modo in cui un codificatore gestisce i caratteri non validi o il modo in cui un decodificatore gestisce byte non validi.
Avvertimento
Le classi di codifica .NET consentono di archiviare e convertire i dati di tipo carattere. Non devono essere usati per archiviare i dati binari in formato stringa. A seconda della codifica usata, la conversione di dati binari in formato stringa con le classi di codifica può introdurre un comportamento imprevisto e produrre dati non accurati o danneggiati. Per convertire i dati binari in un formato stringa, utilizzare il metodo Convert.ToBase64String.
Tutte le classi di codifica dei caratteri in .NET ereditano dalla classe System.Text.Encoding, ovvero una classe astratta che definisce la funzionalità comune a tutte le codifiche dei caratteri. Per accedere ai singoli oggetti di codifica implementati in .NET, eseguire le operazioni seguenti:
Usare le proprietà statiche della classe Encoding, che restituiscono oggetti che rappresentano le codifiche dei caratteri standard disponibili in .NET (ASCII, UTF-7, UTF-8, UTF-16 e UTF-32). Ad esempio, la proprietà Encoding.Unicode restituisce un oggetto UnicodeEncoding. Ogni oggetto usa il fallback sostitutivo per gestire stringhe che non possono codificare e byte che non è in grado di decodificare. Per altre informazioni, vedere fallback di sostituzione.
Chiamare il costruttore della classe di codifica. Gli oggetti per le codifiche ASCII, UTF-7, UTF-8, UTF-16 e UTF-32 possono essere creati in questo modo. Per impostazione predefinita, ogni oggetto usa il fallback sostitutivo per gestire stringhe che non possono essere codificate e byte che non possono essere decodificati, ma è possibile specificare che deve essere generata un'eccezione. Per altre informazioni, vedere il fallback di sostituzione e il fallback di eccezione .
Chiamare il costruttore Encoding(Int32) e passarlo un numero intero che rappresenta la codifica. Gli oggetti di codifica standard usano il fallback sostitutivo e la tabella codici e gli oggetti di codifica DBCS (Double-Byte Character Set) usano il fallback più adatto per gestire stringhe che non possono codificare e byte che non possono decodificare. Per altre informazioni, vedere soluzione alternativa ottimale.
Chiamare il metodo Encoding.GetEncoding, che restituisce qualsiasi codifica standard, pagina di codice o DBCS disponibile in .NET. Gli overload consentono di specificare un oggetto di fallback sia per il codificatore che per il decodificatore.
È possibile recuperare informazioni su tutte le codifiche disponibili in .NET chiamando il metodo Encoding.GetEncodings. .NET supporta gli schemi di codifica dei caratteri elencati nella tabella seguente.
| Classe Encoding | Descrizione |
|---|---|
| ASCII | Codifica un intervallo limitato di caratteri usando i sette bit inferiori di un byte. Poiché questa codifica supporta solo i valori di carattere da U+0000 a U+007F, nella maggior parte dei casi è inadeguato per le applicazioni internazionalizzate. |
| UTF-7 | Rappresenta i caratteri come sequenze di caratteri ASCII a 7 bit. I caratteri Unicode non ASCII sono rappresentati da una sequenza di escape di caratteri ASCII. UTF-7 supporta protocolli come posta elettronica e newsgroup. Tuttavia, UTF-7 non è particolarmente sicuro o affidabile. In alcuni casi, la modifica di un bit può modificare radicalmente l'interpretazione di un'intera stringa UTF-7. In altri casi, stringhe UTF-7 diverse possono codificare lo stesso testo. Per le sequenze che includono caratteri non ASCII, UTF-7 richiede più spazio rispetto a UTF-8 e la codifica/decodifica è più lenta. Di conseguenza, è consigliabile usare UTF-8 anziché UTF-7, se possibile. |
| UTF-8 | Rappresenta ogni punto di codice Unicode come sequenza da uno a quattro byte. UTF-8 supporta le dimensioni dei dati a 8 bit e funziona bene con molti sistemi operativi esistenti. Per l'intervallo di caratteri ASCII, UTF-8 è identico alla codifica ASCII e consente un set di caratteri più ampio. Tuttavia, per gli script cinese-Japanese-Korean (CJK), UTF-8 può richiedere tre byte per ogni carattere e può causare dimensioni di dati maggiori rispetto a UTF-16. A volte la quantità di dati ASCII, ad esempio i tag HTML, giustifica le dimensioni aumentate per l'intervallo CJK. |
| UTF-16 | Rappresenta ogni punto di codice Unicode come sequenza di uno o due interi a 16 bit. I caratteri Unicode più comuni richiedono un solo punto di codice UTF-16, anche se i caratteri supplementari Unicode (U+10000 e versioni successive) richiedono due punti di codice surrogato UTF-16. Sono supportati sia gli ordinamenti di byte little-endian che quelli big-endian. La codifica UTF-16 viene usata da Common Language Runtime per rappresentare Char e String valori e viene usata dal sistema operativo Windows per rappresentare WCHAR valori. |
| UTF-32 | Rappresenta ogni punto di codice Unicode come intero a 32 bit. Sono supportati sia gli ordinamenti di byte little-endian che quelli big-endian. La codifica UTF-32 viene usata quando le applicazioni vogliono evitare il comportamento del punto di codice surrogato della codifica UTF-16 nei sistemi operativi per cui lo spazio codificato è troppo importante. I glifi singoli resi su uno schermo possono comunque essere codificati con più di un carattere UTF-32. |
| Codifica ANSI/ISO | Fornisce supporto per un'ampia gamma di pagine di codici. Nei sistemi operativi Windows, le tabelle codici vengono usate per supportare una lingua o un gruppo di lingue specifico. Per una tabella che elenca le tabelle codici supportate da .NET, vedere la classe Encoding. È possibile recuperare un oggetto di codifica per una tabella codici specifica chiamando il metodo Encoding.GetEncoding(Int32). Una tabella codici contiene 256 punti di codice ed è in base zero. Nella maggior parte delle tabelle codici, i punti di codice da 0 a 127 rappresentano il set di caratteri ASCII e i punti di codice da 128 a 255 differiscono in modo significativo tra le tabelle codici. Ad esempio, la tabella codici 1252 fornisce i caratteri per i sistemi di scrittura latini, tra cui inglese, tedesco e francese. Gli ultimi 128 punti di codice nella tabella codici 1252 contengono i caratteri accenti. La tabella codici 1253 fornisce codici carattere necessari nel sistema di scrittura greco. Gli ultimi 128 punti di codice nella tabella codici 1253 contengono i caratteri greci. Di conseguenza, un'applicazione che si basa su tabelle codici ANSI non può archiviare greco e tedesco nello stesso flusso di testo, a meno che non includa un identificatore che indica la tabella codici di riferimento. |
| Codifiche DBCS (Double-Byte Character Set) | Supporta lingue, ad esempio cinese, giapponese e coreano, che contengono più di 256 caratteri. In un DBCS, una coppia di punti di codice (un byte doppio) rappresenta ogni carattere. La proprietà Encoding.IsSingleByte restituisce false per le codifiche DBCS. È possibile recuperare un oggetto di codifica per un determinato DBCS chiamando il metodo Encoding.GetEncoding(Int32). Quando un'applicazione gestisce i dati DBCS, il primo byte di un carattere DBCS (byte lead) viene elaborato in combinazione con il byte finale che lo segue immediatamente. Poiché una singola coppia di punti di codice a byte doppio può rappresentare caratteri diversi a seconda della tabella codici, questo schema non consente ancora la combinazione di due lingue, ad esempio giapponese e cinese, nello stesso flusso di dati. |
Queste codifiche consentono di usare i caratteri Unicode e le codifiche più comunemente usate nelle applicazioni legacy. Inoltre, è possibile creare una codifica personalizzata definendo una classe che deriva da Encoding ed eseguendo l'override dei relativi membri.
Supporto della codifica .NET Core
Per impostazione predefinita, .NET Core non rende disponibili codifiche della tabella codici diverse dalla tabella codici 28591 e dalle codifiche Unicode, ad esempio UTF-8 e UTF-16. Tuttavia, è possibile aggiungere le codifiche delle pagine di codice disponibili nelle app standard di Windows che utilizzano .NET alla tua app. Per altre informazioni, vedere l'argomento CodePagesEncodingProvider.
Selezione di una classe di codifica
Se è possibile scegliere la codifica da usare dall'applicazione, è consigliabile usare una codifica Unicode, preferibilmente UTF8Encoding o UnicodeEncoding. (.NET supporta anche una terza codifica Unicode, UTF32Encoding.)
Se si prevede di usare una codifica ASCII (ASCIIEncoding), scegliere UTF8Encoding. Le due codifiche sono identiche per il set di caratteri ASCII, ma UTF8Encoding presenta i vantaggi seguenti:
Può rappresentare ogni carattere Unicode, mentre ASCIIEncoding supporta solo i valori di carattere Unicode compresi tra U+0000 e U+007F.
Fornisce il rilevamento degli errori e una maggiore sicurezza.
È stato ottimizzato per essere il più veloce possibile e dovrebbe essere più veloce di qualsiasi altra codifica. Anche per il contenuto interamente ASCII, le operazioni eseguite con UTF8Encoding sono più veloci rispetto alle operazioni eseguite con ASCIIEncoding.
È consigliabile usare ASCIIEncoding solo per le applicazioni legacy. Tuttavia, anche per le applicazioni legacy, UTF8Encoding potrebbe essere una scelta migliore per i motivi seguenti (presupponendo le impostazioni predefinite):
Se l'applicazione include contenuto non strettamente ASCII e lo codifica con ASCIIEncoding, ogni carattere non ASCII viene codificato come punto interrogativo (?). Se l'applicazione decodifica questi dati, le informazioni andranno perse.
Se l'applicazione ha contenuto non strettamente ASCII e lo codifica con UTF8Encoding, il risultato sembra incomprensibile se interpretato come ASCII. Tuttavia, se l'applicazione usa un decodificatore UTF-8 per decodificare questi dati, il ritorno dei dati avviene con successo.
In un'applicazione Web, i caratteri inviati al client in risposta a una richiesta Web devono riflettere la codifica usata nel client. Nella maggior parte dei casi, è necessario impostare la proprietà HttpResponse.ContentEncoding sul valore restituito dalla proprietà HttpRequest.ContentEncoding per visualizzare il testo nella codifica prevista dall'utente.
Utilizzo di un oggetto di codifica
Un codificatore converte una stringa di caratteri (più comunemente, caratteri Unicode) nell'equivalente numerico (byte). Ad esempio, è possibile usare un codificatore ASCII per convertire i caratteri Unicode in ASCII in modo che possano essere visualizzati nella console. Per eseguire la conversione, chiamare il metodo Encoding.GetBytes. Per determinare il numero di byte necessari per archiviare i caratteri codificati prima di eseguire la codifica, è possibile chiamare il metodo GetByteCount.
Nell'esempio seguente viene utilizzata una singola matrice di byte per codificare stringhe in due operazioni separate. Mantiene un indice che indica la posizione iniziale nella matrice di byte per il set successivo di byte codificati ASCII. Chiama il metodo ASCIIEncoding.GetByteCount(String) per garantire che la matrice di byte sia sufficientemente grande da contenere la stringa codificata. Chiama quindi il metodo ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) per codificare i caratteri nella stringa.
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($" {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($"{ShowByteValues(bytes, index)}");
Console.WriteLine();
// Decode Unicode byte array to a string.
string newString = asciiEncoding.GetString(bytes, 0, index);
Console.WriteLine($"Decoded: {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.
Un decodificatore converte una matrice di byte che riflette una particolare codifica dei caratteri in un set di caratteri, in una matrice di caratteri o in una stringa. Per decodificare una matrice di byte in una matrice di caratteri, chiamare il metodo Encoding.GetChars. Per decodificare una matrice di byte in una stringa, chiamare il metodo GetString. Per determinare il numero di caratteri necessari per archiviare i byte decodificati prima di eseguire la decodifica, è possibile chiamare il metodo GetCharCount.
Nell'esempio seguente vengono codificate tre stringhe e quindi decodificate in una singola matrice di caratteri. Mantiene un indice che indica la posizione iniziale nella matrice di caratteri per il set successivo di caratteri decodificati. Chiama il metodo GetCharCount per garantire che la matrice di caratteri sia sufficientemente grande per contenere tutti i caratteri decodificati. Chiama quindi il metodo ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) per decodificare la matrice di byte.
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: {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.
I metodi di codifica e decodifica di una classe derivata da Encoding sono progettati per lavorare su un set completo di dati; ovvero, tutti i dati da codificare o decodificare vengono forniti in una singola chiamata al metodo. In alcuni casi, tuttavia, i dati sono disponibili in un flusso e i dati da codificare o decodificare possono essere disponibili solo da operazioni di lettura separate. Ciò richiede l'operazione di codifica o decodifica per ricordare qualsiasi stato salvato dalla chiamata precedente. I metodi delle classi derivate da Encoder e Decoder sono in grado di gestire operazioni di codifica e decodifica che si estendono su più chiamate di metodo.
Un oggetto Encoder per una particolare codifica è disponibile dalla proprietà Encoding.GetEncoder della codifica. Un oggetto Decoder per una particolare codifica è disponibile dalla proprietà Encoding.GetDecoder della codifica. Per le operazioni di decodifica, si noti che le classi derivate da Decoder includono un metodo Decoder.GetChars, ma non hanno un metodo che corrisponde a Encoding.GetString.
Nell'esempio seguente viene illustrata la differenza tra l'uso dei metodi Encoding.GetString e Decoder.GetChars per decodificare una matrice di byte Unicode. Nell'esempio viene codificata una stringa contenente alcuni caratteri Unicode in un file e quindi vengono utilizzati i due metodi di decodifica per decodificarli dieci byte alla volta. Poiché una coppia di surrogati si verifica nel decimo e nell'undicesimo byte, viene decodificata in chiamate di metodo separate. Come illustrato nell'output, il metodo Encoding.GetString non è in grado di decodificare correttamente i byte e li sostituisce invece con U+FFFD (REPLACEMENT CHARACTER). D'altra parte, il metodo Decoder.GetChars è in grado di decodificare correttamente la matrice di byte per ottenere la stringa originale.
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: {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
Scelta di una strategia di fallback
Quando un metodo tenta di codificare o decodificare un carattere ma non esiste alcun mapping, deve implementare una strategia di fallback che determina come deve essere gestito il mapping non riuscito. Esistono tre tipi di strategie di fallback:
Fallback più adatto
Alternativa di sostituzione
Gestione alternativa delle eccezioni
Importante
I problemi più comuni nelle operazioni di codifica si verificano quando non è possibile eseguire il mapping di un carattere Unicode a una determinata codifica della tabella codici. I problemi più comuni nelle operazioni di decodifica si verificano quando le sequenze di byte non valide non possono essere convertite in caratteri Unicode validi. Per questi motivi, è necessario conoscere la strategia di fallback usata da un oggetto di codifica specifico. Quando possibile, è necessario specificare la strategia di fallback usata da un oggetto di codifica quando si crea un'istanza dell'oggetto.
Riserva Migliore Adattamento
Quando un carattere non ha una corrispondenza esatta nella codifica di destinazione, il codificatore può provare a eseguirne il mapping a un carattere simile. Il "fallback più adatto" (best-fit fallback) è principalmente una questione di codifica piuttosto che un problema di decodifica. Esistono pochissime pagine di codice che contengono caratteri che non possono essere mappati correttamente su Unicode. Il "fallback più adatto" è l'impostazione predefinita per le pagine di codice e le codifiche dei set di caratteri a doppio byte recuperate dagli overload Encoding.GetEncoding(Int32) e Encoding.GetEncoding(String).
Nota
In teoria, le classi di codifica Unicode fornite in .NET (UTF8Encoding, UnicodeEncodinge UTF32Encoding) supportano ogni carattere in ogni set di caratteri, in modo che possano essere usati per eliminare i problemi di fallback più adatti.
Le strategie più adatte variano in base alle diverse tabelle codici. Ad esempio, per alcune pagine di codice, i caratteri latini a larghezza intera vengono mappati ai caratteri latini più comuni a metà larghezza. Per altre pagine di codice, questa mappatura non viene eseguita. Anche in una strategia aggressiva di miglior adattamento, non esiste nessun adattamento immaginabile per alcuni caratteri in alcune codifiche. Ad esempio, un ideografo cinese non ha una mappatura ragionevole alla pagina di codice 1252. In questo caso viene usata una stringa di sostituzione. Per impostazione predefinita, questa stringa è solo un punto interrogativo (U+003F).
Nota
Le strategie più adatte non sono documentate in dettaglio. Tuttavia, diverse pagine di codice sono documentate sul sito web del Unicode Consortium. Esaminare il file readme.txt in tale cartella per una descrizione di come interpretare i file di mapping.
L'esempio seguente utilizza la tabella dei codici 1252 (la tabella codici di Windows per le lingue dell'Europa occidentale) per illustrare il mapping più adatto e i relativi svantaggi. Il metodo Encoding.GetEncoding(Int32) viene utilizzato per recuperare un oggetto di codifica per la tabella codici 1252. Per impostazione predefinita, usa una mappatura più adeguata per i caratteri Unicode che non supporta. Nell'esempio viene creata un'istanza di una stringa contenente tre caratteri non ASCII - CIRCLED LATIN MAIUSC LETTER S (U+24C8), SUPERSCRIPT FIVE (U+2075) e INFINITY (U+221E), separati da spazi. Come illustrato nell'output dell'esempio, quando la stringa è codificata, i tre caratteri originali non spaziali vengono sostituiti da QUESTION MARK (U+003F), DIGIT FIVE (U+0035) e DIGIT EIGHT (U+0038). DIGIT EIGHT non è una soluzione ideale per sostituire il carattere INFINITY non supportato, e QUESTION MARK indica che non era disponibile alcun mapping per il carattere originale.
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: {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
La mappatura migliore è il comportamento predefinito per un oggetto Encoding che codifica i dati Unicode nei dati della pagina di codici, e ci sono applicazioni legacy che si basano su questo comportamento. Tuttavia, la maggior parte delle nuove applicazioni deve evitare il comportamento più adatto per motivi di sicurezza. Ad esempio, le applicazioni non devono inserire un nome di dominio tramite una codifica più adatta.
Nota
È anche possibile implementare un mapping di fallback più appropriato personalizzato per una codifica. Per altre informazioni, vedere la sezione Implementazione di una strategia di fallback personalizzata.
Se il fallback più adatto è l'impostazione predefinita per un oggetto di codifica, è possibile scegliere un'altra strategia di fallback quando si recupera un oggetto Encoding chiamando l'overload Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) o Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). La sezione seguente include un esempio che sostituisce ogni carattere di cui non è possibile eseguire il mapping alla tabella codici 1252 con un 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: {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
Opzione di riserva per la sostituzione
Quando un carattere non ha una corrispondenza esatta nello schema di destinazione, ma non esiste un carattere appropriato a cui è possibile eseguire il mapping, l'applicazione può specificare un carattere o una stringa di sostituzione. Si tratta del comportamento predefinito per il decodificatore Unicode, che sostituisce qualsiasi sequenza a due byte che non può decodificare con REPLACEMENT_CHARACTER (U+FFFD). È anche il comportamento predefinito della classe ASCIIEncoding, che sostituisce ogni carattere che non può codificare o decodificare con un punto interrogativo. Nell'esempio seguente viene illustrata la sostituzione dei caratteri per la stringa Unicode dell'esempio precedente. Come illustrato nell'output, ogni carattere che non può essere decodificato in un valore di byte ASCII viene sostituito da 0x3F, ovvero il codice ASCII per un punto interrogativo.
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: {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
.NET include le classi EncoderReplacementFallback e DecoderReplacementFallback, che sostituiscono una stringa di sostituzione se un carattere non esegue il mapping esattamente in un'operazione di codifica o decodifica. Per impostazione predefinita, questa stringa di sostituzione è un punto interrogativo, ma è possibile utilizzare un overload del costruttore della classe per scegliere una stringa diversa. In genere, la stringa di sostituzione è un singolo carattere, anche se questo non è un requisito. Nell'esempio seguente viene modificato il comportamento del codificatore della pagina codice 1252 creando un'istanza di un oggetto EncoderReplacementFallback in cui si utilizza un asterisco (*) come stringa di sostituzione.
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: {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
È anche possibile implementare una classe sostitutiva per una codifica. Per altre informazioni, vedere la sezione Implementazione di una strategia di fallback personalizzata.
Oltre a QUESTION MARK (U+003F), il CARATTERE DI SOSTITUZIONE Unicode (U+FFFD) viene comunemente usato come stringa di sostituzione, in particolare quando si decodificano sequenze di byte che non possono essere convertite correttamente in caratteri Unicode. Tuttavia, è possibile scegliere qualsiasi stringa di sostituzione e può contenere più caratteri.
Fallback delle eccezioni
Anziché fornire un fallback più adatto o una stringa di sostituzione, un codificatore può generare un EncoderFallbackException se non è in grado di codificare un set di caratteri e un decodificatore può generare un DecoderFallbackException se non è in grado di decodificare una matrice di byte. Per generare un'eccezione nelle operazioni di codifica e decodifica, specificare rispettivamente un oggetto EncoderExceptionFallback e un oggetto DecoderExceptionFallback al metodo Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). Nell'esempio seguente viene illustrato il meccanismo di fallback delle eccezioni con la classe ASCIIEncoding.
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{Convert.ToUInt16(e.CharUnknownHigh):X4} 0x{Convert.ToUInt16(e.CharUnknownLow):X3} at index {e.Index}.");
else
Console.WriteLine($"Unable to encode 0x{Convert.ToUInt16(e.CharUnknown):X4} at index {e.Index}.");
return;
}
Console.WriteLine();
// Decode the ASCII bytes.
try {
string str2 = enc.GetString(bytes);
Console.WriteLine($"Round-trip: {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 {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
È anche possibile implementare un gestore eccezioni personalizzato per un'operazione di codifica. Per altre informazioni, vedere la sezione Implementazione di una strategia di fallback personalizzata.
Gli oggetti EncoderFallbackException e DecoderFallbackException forniscono le informazioni seguenti sulla condizione che ha causato l'eccezione:
L'oggetto EncoderFallbackException include un metodo IsUnknownSurrogate, che indica se il carattere o i caratteri che non possono essere codificati rappresentano una coppia di surrogati sconosciuti (nel qual caso, il metodo restituisce
true) o un singolo carattere sconosciuto (nel qual caso, il metodo restituiscefalse). I caratteri nella coppia di surrogati sono disponibili dalle proprietà EncoderFallbackException.CharUnknownHigh e EncoderFallbackException.CharUnknownLow. Il carattere singolo sconosciuto è disponibile nella proprietà EncoderFallbackException.CharUnknown. La proprietà EncoderFallbackException.Index indica la posizione nella stringa in cui è stato trovato il primo carattere che non è stato possibile codificare.L'oggetto DecoderFallbackException include una proprietà BytesUnknown che restituisce una matrice di byte che non può essere decodificata. La proprietà DecoderFallbackException.Index indica la posizione iniziale dei byte sconosciuti.
Anche se gli oggetti EncoderFallbackException e DecoderFallbackException forniscono informazioni diagnostiche adeguate sull'eccezione, non forniscono l'accesso alla codifica o al buffer di decodifica. Pertanto, non consentono di sostituire o correggere dati non validi all'interno del metodo di codifica o decodifica.
Implementazione di una strategia di fallback personalizzata
Oltre al mapping più adatto implementato internamente dalle pagine di codifica, .NET include le seguenti classi per implementare una strategia di fallback:
Usare EncoderReplacementFallback e EncoderReplacementFallbackBuffer per sostituire i caratteri nelle operazioni di codifica.
Usare DecoderReplacementFallback e DecoderReplacementFallbackBuffer per sostituire i caratteri nelle operazioni di decodifica.
Usare EncoderExceptionFallback e EncoderExceptionFallbackBuffer per generare un EncoderFallbackException quando non è possibile codificare un carattere.
Usare DecoderExceptionFallback e DecoderExceptionFallbackBuffer per generare un DecoderFallbackException quando non è possibile decodificare un carattere.
Inoltre, è possibile implementare una soluzione personalizzata che usa il fallback più adatto, il fallback di sostituzione o il fallback delle eccezioni seguendo questa procedura:
Derivare una classe da EncoderFallback per le operazioni di codifica e da DecoderFallback per le operazioni di decodifica.
Derivare una classe da EncoderFallbackBuffer per le operazioni di codifica e da DecoderFallbackBuffer per le operazioni di decodifica.
Per il fallback delle eccezioni, se le classi predefinite EncoderFallbackException e DecoderFallbackException non soddisfano le vostre esigenze, derivare una classe partendo da un oggetto eccezione, ad esempio Exception o ArgumentException.
Derivazione da EncoderFallback o DecoderFallback
Per implementare una soluzione di fallback personalizzata, è necessario creare una classe che eredita da EncoderFallback per le operazioni di codifica e da DecoderFallback per le operazioni di decodifica. Le istanze di queste classi vengono passate al metodo Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) e fungono da intermediario tra la classe di codifica e l'implementazione del fallback.
Quando si crea una soluzione di fallback personalizzata per un codificatore o un decodificatore, è necessario implementare i membri seguenti:
La proprietà EncoderFallback.MaxCharCount o DecoderFallback.MaxCharCount, che restituisce il numero massimo possibile di caratteri che il fallback di sostituzione ottimale, di sostituzione o di eccezioni può restituire per sostituire un singolo carattere. Per un fallback di eccezione personalizzato, il relativo valore è zero.
Metodo EncoderFallback.CreateFallbackBuffer o DecoderFallback.CreateFallbackBuffer, che restituisce l'implementazione di EncoderFallbackBuffer o DecoderFallbackBuffer personalizzata. Il metodo viene chiamato dal codificatore quando rileva il primo carattere che non è in grado di codificare correttamente o dal decodificatore quando rileva il primo byte che non è in grado di decodificare correttamente.
Derivazione da EncoderFallbackBuffer o DecoderFallbackBuffer
Per implementare una soluzione di fallback personalizzata, è necessario creare anche una classe che eredita da EncoderFallbackBuffer per le operazioni di codifica e da DecoderFallbackBuffer per le operazioni di decodifica. Le istanze di queste classi vengono restituite dal metodo CreateFallbackBuffer delle classi EncoderFallback e DecoderFallback. Il metodo EncoderFallback.CreateFallbackBuffer viene chiamato dal codificatore quando rileva il primo carattere che non è in grado di codificare e il metodo DecoderFallback.CreateFallbackBuffer viene chiamato dal decodificatore quando rileva uno o più byte che non è in grado di decodificare. Le classi EncoderFallbackBuffer e DecoderFallbackBuffer forniscono l'implementazione del fallback. Ogni istanza rappresenta un buffer contenente i caratteri di fallback che sostituiranno il carattere che non può essere codificato o la sequenza di byte che non può essere decodificata.
Quando si crea una soluzione di fallback personalizzata per un codificatore o un decodificatore, è necessario implementare i membri seguenti:
Metodo EncoderFallbackBuffer.Fallback o DecoderFallbackBuffer.Fallback. EncoderFallbackBuffer.Fallback viene chiamato dal codificatore per fornire al buffer di fallback informazioni sul carattere che non può codificare. Poiché il carattere da codificare può essere una coppia di surrogati, questo metodo viene sottoposto a overload. Viene passato un overload al carattere da codificare e al relativo indice nella stringa. Il secondo overload viene passato al surrogato alto e basso insieme al relativo indice nella stringa. Il metodo DecoderFallbackBuffer.Fallback viene chiamato dal decodificatore per fornire al buffer di fallback informazioni sui byte che non è possibile decodificare. Questo metodo viene passato a una matrice di byte che non può decodificare, insieme all'indice del primo byte. Il metodo di fallback deve restituire
truese il buffer di fallback può fornire un carattere o caratteri di sostituzione ottimali; in caso contrario, deve restituirefalse. Per un fallback di eccezione, il metodo di fallback deve generare un'eccezione.Il metodo EncoderFallbackBuffer.GetNextChar o DecoderFallbackBuffer.GetNextChar, chiamato ripetutamente dal codificatore o dal decodificatore per ottenere il carattere successivo dal buffer di fallback. Quando vengono restituiti tutti i caratteri di fallback, il metodo deve restituire U+0000.
Proprietà EncoderFallbackBuffer.Remaining o DecoderFallbackBuffer.Remaining, che restituisce il numero di caratteri rimanenti nel buffer di fallback.
Il metodo EncoderFallbackBuffer.MovePrevious o DecoderFallbackBuffer.MovePrevious, che sposta la posizione corrente nel buffer di fallback al carattere precedente.
Metodo EncoderFallbackBuffer.Reset o DecoderFallbackBuffer.Reset, che reinizializza il buffer di fallback.
Se l'implementazione del fallback è un fallback più adatto o un fallback di sostituzione, le classi derivate da EncoderFallbackBuffer e DecoderFallbackBuffer mantengono anche due campi di istanza privata: il numero esatto di caratteri nel buffer; e l'indice del carattere successivo nel buffer da restituire.
Esempio di EncoderFallback
In un esempio precedente è stato usato il fallback di sostituzione per sostituire i caratteri Unicode che non corrispondono ai caratteri ASCII con un asterisco (*). Nell'esempio seguente viene usata un'implementazione di fallback personalizzata più adatta per fornire una mappatura migliore dei caratteri non ASCII.
Il codice seguente definisce una classe denominata CustomMapper derivata da EncoderFallback per gestire il mapping ottimale dei caratteri non ASCII. Il metodo CreateFallbackBuffer restituisce un oggetto CustomMapperFallbackBuffer, che fornisce l'implementazione EncoderFallbackBuffer. La classe CustomMapper usa un oggetto Dictionary<TKey,TValue> per archiviare i mapping dei caratteri Unicode non supportati (il valore della chiave) e i corrispondenti caratteri a 8 bit ,archiviati in due byte consecutivi in un intero a 64 bit. Per rendere disponibile questo mapping al buffer di fallback, l'istanza di CustomMapper viene passata come parametro al costruttore della classe CustomMapperFallbackBuffer. Poiché il mapping più lungo è la stringa "INF" per il carattere Unicode U+221E, la proprietà MaxCharCount restituisce 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
Il codice seguente definisce la classe CustomMapperFallbackBuffer, derivata da EncoderFallbackBuffer. Il dizionario che contiene i mapping più adatti e definito nell'istanza di CustomMapper è disponibile dal relativo costruttore di classe. Il metodo Fallback restituisce true se uno dei caratteri Unicode che il codificatore ASCII non può codificare è definito nel dizionario di mapping; in caso contrario, restituisce false. Per ogni fallback, la variabile count privata indica il numero di caratteri che rimangono da restituire e la variabile index privata indica la posizione nel buffer di stringa, charsToReturn, del carattere successivo da restituire.
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
Il codice seguente crea quindi un'istanza dell'oggetto CustomMapper e ne passa un'istanza al metodo Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). L'output indica che l'implementazione di fallback più adatta gestisce correttamente i tre caratteri non ASCII nella stringa originale.
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: {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