Condividi tramite


Codifica di caratteri in .NET Framework

I caratteri sono entità astratte che possono essere rappresentate in molti modi diversi. La codifica dei caratteri è un sistema mediante il quale ciascun carattere di un set di caratteri supportati viene associato a un determinato valore che rappresenta tale carattere. Il codice Morse, ad esempio, è una codifica dei caratteri in cui ciascun carattere dell'alfabeto romano viene associato a un modello di punti e trattini adatti per la trasmissione tramite linee telegrafiche. La codifica dei caratteri per computer associa ciascun carattere di un set di caratteri supportati a un valore numerico che rappresenta tale carattere. La codifica dei caratteri è costituita da due componenti distinti:

  • Un codificatore che converte una sequenza di caratteri in una sequenza di valori numerici (byte).

  • Un decodificatore che converte una sequenza di byte in una sequenza di caratteri.

La codifica dei caratteri descrive le regole in base alle quali operano un codificatore e un decodificatore. La classe UTF8Encoding, ad esempio, descrive le regole per la codifica e la decodifica in UTF-8 (Unicode Transformation Format), in cui vengono utilizzati da uno a quattro byte per rappresentare un singolo carattere Unicode. La codifica e la decodifica possono includere anche la convalida. La classe UnicodeEncoding, ad esempio, verifica tutti i surrogati per assicurarsi che costituiscano coppie di surrogati valide. Una coppia di surrogati è costituita da un carattere con un punto di codice compreso tra U+D800 e U+DBFF seguito da un carattere con un punto di codice compreso tra U+DC00 e U+DFFF. Una strategia di fallback determina il modo in cui un codificatore gestisce i caratteri non validi o il modo in cui un decodificatore gestisce i byte non validi.

Nota di avvisoAttenzione

Le classi di codifica di .NET Framework consentono di archiviare e convertire i dati di tipo carattere.Non devono essere utilizzate per archiviare i dati binari in formato stringa.In base alla codifica utilizzata, la conversione dei dati binari in formato stringa con le classi di codifica può generare un comportamento imprevisto e produrre dati non accurati o danneggiati.Per convertire i dati binari in formato stringa, utilizzare il metodo Convert.ToBase64String(Byte[]).

Le applicazioni progettate per Common Language Runtime utilizzano i codificatori per eseguire il mapping delle rappresentazioni di caratteri Unicode supportate da Common Language Runtime agli altri schemi di codifica e i decodificatori per eseguire il mapping dei caratteri dalle codifiche non Unicode a Unicode.

In questo argomento sono incluse le sezioni seguenti:

Codifiche in .NET Framework

Tutte le classi di codifica dei caratteri in .NET Framework ereditano dalla classe System.Text.Encoding, una classe astratta che definisce le funzionalità comuni a tutte le codifiche dei caratteri. Per accedere ai singoli oggetti di codifica implementati in .NET Framework, effettuare le operazioni seguenti:

  • Utilizzare le proprietà statiche della classe Encoding che restituiscono gli oggetti che rappresentano le codifiche dei caratteri standard disponibili in .NET Framework (ASCII, UTF-7, UTF-8, UTF-16 e UTF-32). La proprietà Encoding.Unicode, ad esempio, restituisce un oggetto UnicodeEncoding. Ogni oggetto utilizza il fallback di sostituzione per gestire le stringhe che non è possibile codificare e i byte che non è possibile decodificare. Per ulteriori informazioni, vedere la sezione Fallback di sostituzione.

  • Chiamare il costruttore di classe della codifica. In questo modo è possibile creare un'istanza degli oggetti per le codifiche ASCII, UTF-7, UTF-8, UTF-16 e UTF-32. Per impostazione predefinita, ogni oggetto utilizza il fallback di sostituzione per gestire le stringhe che non è possibile codificare e i byte che non è possibile decodificare, ma è anche possibile specificare di generare un'eccezione. Per ulteriori informazioni, vedere le sezioni Fallback di sostituzione e Fallback di eccezione.

  • Chiamare il costruttore Encoding.Encoding(Int32) e passare un intero che rappresenta la codifica. Gli oggetti di codifica standard utilizzano il fallback di sostituzione mentre gli oggetti di codifica del set di caratteri DBCS (Double-Byte Character Set) e della tabella codici utilizzano il fallback con mapping più appropriato per gestire le stringhe che non è possibile codificare e i byte che non è possibile decodificare. Per ulteriori informazioni, vedere la sezione Fallback con mapping più appropriato.

  • Chiamare il metodo Encoding.GetEncoding che restituisce qualsiasi codifica standard, della tabella codici o DBCS disponibile in .NET Framework. Gli overload consentono di specificare un oggetto di fallback sia per il codificatore che per il decodificatore.

NotaNota

Secondo lo standard Unicode viene assegnato un punto di codice, ovvero un numero, e un nome a ciascun carattere di ogni script supportato.Il carattere "A", ad esempio, è rappresentato dal punto di codice U+0041 e dal nome "LATIN CAPITAL LETTER A".Le codifiche UTF (Unicode Transformation Format) definiscono le modalità per codificare il punto di codice in una sequenza di uno o più byte.Lo schema di codifica Unicode semplifica lo sviluppo di applicazioni internazionali in quanto consente la rappresentazione dei caratteri da qualsiasi set di caratteri in una singola codifica.Gli sviluppatori di applicazioni non devono più tenere traccia dello schema di codifica che era stato utilizzato per produrre i caratteri per una lingua o un sistema di scrittura specifico e i dati possono essere condivisi tra i sistemi a livello internazionale senza che vengano danneggiati.

In .NET Framework sono supportate tre codifiche definite dallo standard Unicode: UTF-8, UTF-16 e UTF-32.Per ulteriori informazioni sullo standard Unicode, vedere la home page dello standard Unicode (informazioni in lingua inglese).

È possibile recuperare le informazioni su tutte le codifiche disponibili in .NET Framework chiamando il metodo Encoding.GetEncodings. In .NET Framework sono supportati i sistemi di codifica dei caratteri elencati nella tabella seguente.

Codifica

Classe

Descrizione

Vantaggi/svantaggi

ASCII

[ T:System.Text.ASCIIEncoding ]

Codifica una gamma limitata di caratteri utilizzando i sette bit meno significativi di un byte.

Poiché vengono supportati solo i valori di caratteri compresi tra U+0000 e U+007F, nella maggior parte dei casi questa codifica non è adatta alle applicazioni internazionali.

UTF-7

[ T:System.Text.UTF7Encoding ]

Rappresenta i caratteri come una sequenza di caratteri ASCII a 7 bit. I caratteri Unicode non ASCII sono rappresentati da una sequenza di escape di caratteri ASCII.

UTF-7 supporta i protocolli come, ad esempio, i protocolli di posta elettronica e newsgroup. La codifica UTF-7 non è tuttavia particolarmente sicura o affidabile. In alcuni casi, il cambiamento di un bit può alterare radicalmente l'interpretazione di un'intera stringa UTF-7. In altri casi, stringhe UTF-7 diverse possono codificare lo stesso testo. Per le sequenze in cui sono inclusi caratteri non ASCII, il formato UTF-7 richiede più spazio rispetto a UTF-8 e le operazioni di codifica e decodifica sono più lente. Se possibile, è pertanto necessario utilizzare la codifica UTF-8 anziché UTF-7.

UTF-8

[ T:System.Text.UTF8Encoding ]

Rappresenta ogni punto di codice Unicode come una sequenza da uno a quattro byte.

Il formato UTF-8 supporta le dimensioni dei dati a 8 bit ed è compatibile con molti sistemi operativi esistenti. Per l'intervallo di caratteri ASCII, la codifica UTF-8 è identica alla codifica ASCII e consente di utilizzare un set di caratteri più ampio. Per gli script CJK (Chinese-Japanese-Korean), ovvero cinese, giapponese e coreano, tuttavia, la codifica UTF-8 può richiedere anche tre byte per ciascun carattere, dando luogo a dimensioni dei dati potenzialmente maggiori rispetto alla codifica UTF-16. Si noti che talvolta la quantità di dati ASCII, ad esempio i tag HTML, giustifica l'aumento delle dimensioni dell'intervallo CJK.

UTF-16

[ T:System.Text.UnicodeEncoding ]

Rappresenta ogni punto di codice Unicode come una sequenza di uno o due interi a 16 bit. I caratteri Unicode più comuni richiedono un solo punto di codice UTF-16, sebbene i caratteri Unicode supplementari, a partire da U+10000, richiedano due punti di codice surrogati UTF-16. Sono supportati sia gli ordini dei byte little-endian che big-endian.

La codifica UTF-16 viene utilizzata da Common Language Runtime per rappresentare i valori String e Char e viene utilizzata dal sistema operativo Windows per rappresentare i valori WCHAR.

UTF-32

[ T:System.Text.UTF32Encoding ]

Rappresenta ogni punto di codice Unicode come un intero a 32 bit. Sono supportati sia gli ordini dei byte little-endian che big-endian.

La codifica UTF-32 viene utilizzata quando è necessario evitare il comportamento del punto di codice surrogato della codifica UTF-16 su sistemi operativi per i quali lo spazio codificato è estremamente importante. I soli glifi di cui viene eseguito il rendering su uno schermo possono comunque essere codificati con più di un carattere UTF-32.

Codifiche ANSI/ISO

Fornisce il supporto per diverse tabelle codici. Nei sistemi operativi Windows le tabelle codici vengono utilizzate per supportare una lingua o un gruppo di lingue specifico. Per un elenco delle tabelle codici supportate da .NET Framework, 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 compresi tra 0 e 127 rappresentano il set di caratteri ASCII, mentre i punti di codice compresi tra 128 e 255 differiscono in modo significativo tra le diverse tabelle codici. La tabella codici 1252, ad esempio, fornisce i caratteri per i sistemi di scrittura con caratteri latini, inclusi l'inglese, il tedesco e il francese. Gli ultimi 128 punti di codice nella tabella codici 1252 contengono i caratteri accentati. La tabella codici 1253 fornisce i codici di caratteri necessari nel sistema di scrittura della lingua greca. Gli ultimi 128 punti di codice nella tabella codici 1253 contengono i caratteri greci. Non è quindi possibile per un'applicazione che si basa sulle tabelle codici ASCII memorizzare la lingua greca e la lingua tedesca nello stesso flusso di testo, a meno che non venga incluso un identificatore indicante la tabella codici a cui si fa riferimento.

Codifiche DBCS (Double Byte Character Set)

Supporta le lingue quali il cinese, il giapponese e il coreano che contengono più di 256 caratteri. Nello schema DBCS ciascun carattere è rappresentato da una coppia di punti di codice (un byte doppio). La proprietà Encoding.IsSingleByte restituisce false per le codifiche DBCS. È possibile recuperare un oggetto di codifica per una codifica DBCS specifica chiamando il metodo Encoding.GetEncoding(Int32).

Nello schema DBCS ciascun carattere è rappresentato da una coppia di punti di codice (un byte doppio). Quando un'applicazione gestisce i dati DBCS, il primo byte di un carattere DBCS (byte apertura) viene elaborato insieme al byte di chiusura immediatamente successivo. 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 il giapponese e il cinese, nello stesso flusso di dati.

Tali codifiche consentono di utilizzare i caratteri Unicode, nonché le codifiche utilizzate più di frequente nelle applicazioni legacy. Inoltre, è possibile creare una codifica personalizzata definendo una classe che deriva da Encoding ed eseguendo l'override dei relativi membri.

Selezione di una classe di codifica

Se è possibile scegliere la codifica da utilizzare nell'applicazione, è consigliabile utilizzare una codifica Unicode, preferibilmente UTF8Encoding o UnicodeEncoding. .NET Framework supporta anche una terza codifica Unicode, UTF32Encoding.

Se si intende utilizzare 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 soltanto i valori dei caratteri Unicode tra U+0000 e U+007F.

  • Fornisce il rilevamento degli errori e una migliore sicurezza.

  • È stata ottimizzata in modo da essere il più veloce possibile e dovrebbe essere più rapida di qualsiasi altra codifica. Anche se il contenuto è interamente ASCII, le operazioni eseguite con UTF8Encoding risultano più veloci delle operazioni eseguite con ASCIIEncoding.

Prendere in considerazione l'eventualità di utilizzare la codifica ASCIIEncoding solo per le applicazioni legacy. Tuttavia, anche per le applicazioni legacy UTF8Encoding potrebbe rappresentare una scelta migliore per i motivi seguenti, supponendo l'utilizzo delle impostazioni predefinite:

  • Se l'applicazione dispone di contenuto non completamente ASCII e viene eseguita la codifica con ASCIIEncoding, ogni carattere non ASCII viene codificato come punto interrogativo (?). La successiva decodifica di tali dati da parte dell'applicazione provoca la perdita delle informazioni.

  • Se l'applicazione dispone di contenuto non completamente ASCII e viene eseguita la codifica con UTF8Encoding, il risultato appare incomprensibile se interpretato come ASCII. Con la successiva decodifica di tali dati da parte dell'applicazione con un decodificatore UTF-8, tuttavia, viene eseguito un round trip corretto.

In un'applicazione Web i caratteri inviati al client in risposta a una richiesta Web devono riflettere la codifica utilizzata dal client. Nella maggior parte dei casi, perché il testo venga visualizzato nella codifica prevista dall'utente è consigliabile impostare la proprietà HttpResponse.ContentEncoding sul valore restituito dalla proprietà HttpRequest.ContentEncoding.

Utilizzo di un oggetto di codifica

Un codificatore converte una stringa di caratteri, in genere caratteri Unicode, nell'equivalente numerico (byte). È possibile ad esempio utilizzare un codificatore ASCII per convertire i caratteri Unicode in ASCII affinché possano essere visualizzati nella console. Per eseguire la conversione, è possibile chiamare il metodo Encoding.GetBytes. Se si desidera determinare il numero di byte necessari per memorizzare i caratteri codificati prima di eseguire la codifica, è possibile chiamare il metodo GetByteCount.

Nell'esempio seguente viene utilizzata una matrice a singolo byte per codificare le stringhe in due operazioni distinte. Viene gestito un indice che indica la posizione iniziale nella matrice di byte per il successivo set di byte codificati ASCII. Viene chiamato il metodo ASCIIEncoding.GetByteCount(String) per assicurarsi che le dimensioni della matrice di byte siano sufficienti per contenere la stringa codificata. Viene quindi chiamato il metodo ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) per codificare i caratteri della stringa.

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.
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.

Un decodificatore converte una matrice di byte che riflette una codifica dei caratteri specifica in un set di caratteri all'interno di una matrice di caratteri o di una stringa. Per decodificare una matrice di byte in una matrice di caratteri, è possibile chiamare il metodo Encoding.GetChars. Per decodificare una matrice di byte in una stringa, è possibile chiamare il metodo GetString. Se si desidera determinare il numero di caratteri necessari per memorizzare i byte decodificati prima di eseguire la decodifica, è possibile chiamare il metodo GetCharCount.

Nell'esempio seguente vengono codificate tre stringhe e successivamente decodificate in una singola matrice di caratteri. Viene gestito un indice che indica la posizione iniziale nella matrice di caratteri per il successivo set di caratteri decodificati. Viene chiamato il metodo GetCharCount per assicurarsi che le dimensioni della matrice di caratteri siano sufficienti per contenere tutti i caratteri decodificati. Viene quindi chiamato il metodo ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) per decodificare la matrice di byte.

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.
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.

I metodi di codifica e decodifica di una classe derivata da Encoding sono progettati per essere utilizzati in un set di dati completo, ovvero tutti i dati da codificare o decodificare vengono forniti in una sola chiamata al metodo. Tuttavia, in alcuni casi, i dati sono disponibili in un flusso e i dati da codificare o decodificare possono essere disponibili solo da operazioni di lettura separate. È necessario pertanto che l'operazione di codifica o di decodifica memorizzi lo stato salvato dalla chiamata precedente. I metodi delle classi derivate da Encoder e Decoder possono gestire le operazioni di codifica e di decodifica che si estendono in più chiamate al metodo.

Un oggetto Encoder per una codifica specifica è disponibile dalla proprietà Encoding.GetEncoder di tale codifica. Un oggetto Decoder per una codifica specifica è disponibile dalla proprietà Encoding.GetDecoder di tale codifica. Si noti che per le operazioni di decodifica le classi derivate da Decoder includono un metodo Decoder.GetChars, ma non dispongono di un metodo che corrisponde a Encoding.GetString.

Nell'esempio seguente viene illustrata la differenza tra l'utilizzo dei metodi Encoding.GetChars e Decoder.GetChars per la decodifica di 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 nei byte 10 e 11, essa viene decodificata in chiamate al metodo separate. Come illustrato nell'output, il metodo Encoding.GetChars non è in grado di decodificare correttamente i byte, ma li sostituisce con U+FFFD (REPLACEMENT CHARACTER). Il metodo Decoder.GetChars è invece in grado di decodificare correttamente la matrice di byte per ottenere la stringa originale.

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
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

Scelta di una strategia di fallback

Quando un metodo tenta di codificare o decodificare un carattere in assenza di mapping, è necessario implementare una strategia di fallback che determina come gestire il mapping non riuscito. Esistono tre tipi di strategie di fallback:

  • Fallback con mapping più appropriato

  • Fallback di sostituzione

  • Fallback di eccezione

Nota importanteImportante

I problemi più comuni nelle operazioni di codifica si verificano quando non è possibile eseguire il mapping di un carattere Unicode a una codifica della tabella codici specifica.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, è consigliabile conoscere la strategia di fallback utilizzata da un oggetto di codifica specifico.Se possibile, è consigliabile specificare la strategia di fallback utilizzata da un oggetto di codifica quando viene creata un'istanza dell'oggetto.

Fallback con mapping più appropriato

Quando un carattere non ha una corrispondenza esatta nella codifica di destinazione, il codificatore può tentare di eseguirne il mapping a un carattere simile. Il fallback con mapping più appropriato è soprattutto un problema di codifica, non di decodifica. Il numero delle tabelle codici che contengono caratteri di cui non è possibile eseguire correttamente in mapping a Unicode è veramente limitato. Il fallback con mapping più appropriato è l'impostazione predefinita per le codifiche del set di caratteri DBCS (Double-Byte Character Set) e della tabella codici recuperate dagli overload di Encoding.GetEncoding(String) e Encoding.GetEncoding(Int32).

NotaNota

In teoria, le classi di codifica Unicode fornite in .NET Framework (UTF8Encoding, UnicodeEncoding e UTF32Encoding) supportano ogni carattere di ogni set di caratteri, in modo che possano essere utilizzate per eliminare i problemi del fallback con mapping più appropriato.

Le strategie di fallback con mapping più appropriato variano per le diverse tabelle codici e non vengono trattate in dettaglio. Per alcune tabelle codici, ad esempio, i caratteri dell'alfabeto latino a larghezza intera vengono mappati a caratteri dello stesso alfabeto a metà larghezza più comuni. Per altre tabelle codici tale mapping non ha luogo. Anche applicando una strategia di fallback con mapping più appropriato particolarmente aggressiva, per alcuni caratteri in determinate codifiche non esiste una corrispondenza adatta immaginabile. Per un ideogramma cinese, ad esempio, non è disponibile un mapping ragionevole alla tabella codici 1252. In questo caso, viene utilizzata una stringa sostitutiva. Per impostazione predefinita, questa stringa è costituita semplicemente da un solo carattere QUESTION MARK (U+003F).

Nell'esempio seguente viene utilizzata la tabella codici 1252, ovvero la tabella codici di Windows per le lingue dell'Europa occidentale, per illustrare il mapping più appropriato 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, viene utilizzato un mapping più appropriato per i caratteri Unicode non supportati. Nell'esempio viene creata un'istanza di una stringa contenente tre caratteri non ASCII, CIRCLED LATIN CAPITAL LETTER S (U+24C8), SUPERSCRIPT FIVE (U+2075) e INFINITY (U+221E), separati da spazi. Come illustrato nell'output dall'esempio, quando la stringa viene codificata, i tre caratteri non spazio originali vengono sostituiti da QUESTION MARK (U+003F), DIGIT FIVE (U+0035) e DIGIT EIGHT (U+0038). DIGIT EIGHT è una sostituzione non particolarmente adeguata per il carattere non supportato e QUESTION MARK indica che non vi è alcun mapping disponibile per il carattere originale.

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
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

Il mapping più appropriato è il comportamento predefinito per un oggetto Encoding che codifica i dati Unicode in dati della tabella codici e vi sono applicazioni legacy che si basano su tale comportamento. La maggior parte delle nuove applicazioni deve tuttavia evitare questo tipo di comportamento per motivi di sicurezza. Le applicazioni, ad esempio, devono evitare di sottoporre i nomi di dominio a una codifica con mapping più appropriato.

NotaNota

È anche possibile implementare un fallback con mapping più appropriato personalizzato per una codifica.Per ulteriori informazioni, vedere la sezione Implementazione di una strategia di fallback personalizzata.

Se il fallback con mapping più appropriato è l'impostazione predefinita per un oggetto di codifica, è possibile scegliere un'altra strategia di fallback quando si recupera un oggetto Encoding chiamando l'overload di Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) o Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback). Nella sezione seguente è incluso un esempio in cui ogni carattere di cui non è possibile eseguire il mapping alla tabella codici 1252 viene sostituito con un asterisco (*).

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
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

Fallback di sostituzione

Quando un carattere non ha una corrispondenza esatta nello schema di destinazione, ma non è presente alcun carattere appropriato a cui è possibile eseguire il mapping, l'applicazione può specificare un carattere o una stringa di sostituzione. Si tratta del comportamento predefinito del decodificatore Unicode, che sostituisce qualsiasi sequenza a due byte che non è possibile 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 del carattere per la stringa Unicode dell'esempio precedente. Come illustrato nell'output, ogni carattere che non può essere codificato in un valore byte ASCII viene sostituito da 0x3F, ovvero il codice ASCII per un punto interrogativo.

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
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

In .NET Framework sono incluse le classi DecoderReplacementFallback e EncoderReplacementFallback, che sostituiscono una stringa di sostituzione se non è possibile eseguire esattamente il mapping di un carattere in un'operazione di codifica o di decodifica. Per impostazione predefinita, questa stringa di sostituzione è un punto interrogativo, ma è possibile chiamare l'overload del costruttore di classe per scegliere una stringa diversa. In genere la stringa di sostituzione è un singolo carattere, sebbene non sia un requisito. Nell'esempio seguente viene modificato il comportamento del codificatore 1252 della tabella codici per creare un'istanza di un oggetto EncoderReplacementFallback che utilizza un asterisco (*) come stringa di sostituzione.

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
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
NotaNota

È anche possibile implementare una classe di sostituzione per una codifica.Per ulteriori informazioni, vedere la sezione Implementazione di una strategia di fallback personalizzata.

Oltre a QUESTION MARK (U+003F), viene in genere utilizzato REPLACEMENT CHARACTER (U+FFFD) Unicode come stringa di sostituzione, in particolare quando si decodificano le sequenze di byte che non possono essere convertite correttamente in caratteri Unicode. È tuttavia possibile scegliere qualsiasi stringa di sostituzione, che può contenere più caratteri.

Fallback di eccezione

Anziché fornire un fallback con mapping più appropriato o una stringa di sostituzione, un codificatore può generare un'eccezione EncoderFallbackException se non è in grado di codificare un set di caratteri e un decodificatore può generare un'eccezione DecoderFallbackException se non è in grado di decodificare una matrice di byte. Per generare un'eccezione nelle operazioni di codifica e di decodifica, è necessario fornire rispettivamente un oggetto EncoderExceptionFallback e un oggetto DecoderExceptionFallback al metodo Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). Nell'esempio seguente viene illustrato il fallback di eccezione con la classe ASCIIEncoding.

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.
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.
NotaNota

È anche possibile implementare un gestore di eccezioni personalizzato per un'operazione di codifica.Per ulteriori informazioni, vedere la sezione Implementazione di una strategia di fallback personalizzata.

Gli oggetti DecoderFallbackException e EncoderFallbackException forniscono le seguenti informazioni sulla condizione che ha causato l'eccezione:

Anche se gli oggetti DecoderFallbackException e EncoderFallbackException forniscono informazioni diagnostiche appropriate sull'eccezione, non consentono di accedere al buffer di codifica o di decodifica. Non è pertanto possibile sostituire o correggere i dati non validi all'interno del metodo di codifica o decodifica.

Implementazione di una strategia di fallback personalizzata

Oltre al mapping più appropriato che viene implementato internamente dalle tabelle codici, .NET Framework include le classi seguenti per implementare una strategia di fallback:

Inoltre, è possibile implementare una soluzione personalizzata che utilizza il fallback con mapping più appropriato, il fallback di sostituzione o il fallback di eccezione, attenendosi alla procedura seguente:

  1. Derivare la classe da EncoderFallback per le operazioni di codifica e da DecoderFallback per le operazioni di decodifica.

  2. Derivare la classe da EncoderFallbackBuffer per le operazioni di codifica e da DecoderFallbackBuffer per le operazioni di decodifica.

  3. Per il fallback di eccezione, se le classi predefinite DecoderFallbackException e EncoderFallbackException non soddisfano le specifiche esigenze, derivare una classe 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 di fallback.

Quando si crea una soluzione di fallback personalizzata per un codificatore o decodificatore, è necessario implementare i membri seguenti:

Derivazione da EncoderFallbackBuffer o DecoderFallbackBuffer

Per implementare una soluzione di fallback personalizzata, è inoltre necessario creare 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 viene rilevato il primo carattere che non è possibile codificare e il metodo DecoderFallback.CreateFallbackBuffer viene chiamato dal decodificatore quando viene rilevato uno o più byte che non è possibile decodificare. Le classi EncoderFallbackBuffer e DecoderFallbackBuffer forniscono l'implementazione di fallback. Ogni istanza rappresenta un buffer che contiene 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 decodificatore, è necessario implementare i membri seguenti:

Se l'implementazione di fallback è un fallback con mapping più appropriato o un fallback di sostituzione, le classi derivate da EncoderFallbackBuffer e DecoderFallbackBuffer gestiscono inoltre due campi di istanza privati: il numero esatto di caratteri nel buffer e l'indice del carattere successivo nel buffer da restituire.

Esempio di EncoderFallback

In uno degli esempi precedenti è stato utilizzato il fallback di sostituzione per sostituire i caratteri Unicode che non corrispondevano ai caratteri ASCII con un asterisco (*). Nell'esempio seguente viene utilizzata un'implementazione personalizzata del fallback con mapping più appropriato anziché fornire un mapping più appropriato dei caratteri non ASCII.

Nel codice seguente viene definita una classe denominata CustomMapper derivata da EncoderFallback per gestire il mapping più appropriato dei caratteri non ASCII. Il metodo CreateFallbackBuffer restituisce un oggetto CustomMapperFallbackBuffer che fornisce l'implementazione di EncoderFallbackBuffer. La classe CustomMapper utilizza un oggetto Dictionary<TKey, TValue> per archiviare i mapping dei caratteri Unicode non supportati (valore chiave) e i relativi caratteri corrispondenti a 8 bit, archiviati in due byte consecutivi in un intero a 64 bit. Per rendere il mapping disponibile 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 : 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
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; }
   } 
}

Nel codice seguente viene definita la classe CustomMapperFallbackBuffer derivata dalla classe EncoderFallbackBuffer. Il dizionario definito nell'istanza di CustomMapper e contenente i mapping più appropriati è disponibile dal costruttore della 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 privata count indica il numero di caratteri ancora da restituire mentre la variabile privata index indica la posizione nel buffer di stringa, charsToReturn, del carattere successivo da restituire.

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
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;
   }
}

Nel codice seguente viene creata un'istanza dell'oggetto CustomMapper che viene quindi passata al metodo Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). L'output indica che l'implementazione del fallback con mapping più appropriato gestisce correttamente i tre caratteri non ASCII della stringa originale.

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
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();
      }
   }
}

Vedere anche

Riferimenti

Encoder

Decoder

DecoderFallback

Encoding

EncoderFallback

Altre risorse

Codifica e localizzazione

Cronologia delle modifiche

Data

Cronologia

Motivo

Ottobre 2010

Rivisto in modo esteso.

Miglioramento delle informazioni.