Partager via


Encodage de caractères dans le .NET Framework

Les caractères constituent des entités abstraites qui peuvent être représentées de différentes façons. L'encodage de caractères est un système qui associe chaque caractère d'un jeu de caractères pris en charge à une valeur qui représente ce caractère. Par exemple, le morse est un encodage de caractères qui associe chaque caractère de l'alphabet romain à un modèle de points et de tirets qui peuvent être transmis sur des lignes télégraphiques. Dans le domaine informatique, l'encodage de caractères associe chaque caractère d'un jeu de caractères pris en charge à une valeur numérique qui représente ce caractère. L'encodage de caractères comporte deux éléments distincts :

  • Un encodeur, qui convertit une séquence de caractères en une séquence de valeurs numériques (octets).

  • Un décodeur, qui convertit une séquence d'octets en une séquence de caractères.

L'encodage de caractères décrit les règles de fonctionnement d'un encodeur et d'un décodeur. Par exemple, la classe UTF8Encoding décrit les règles pour l'encodage et le décodage Unicode UTF-8 (Unicode Transformation Format), qui utilise un à quatre octets pour représenter un seul caractère Unicode. L'encodage et le décodage peuvent également inclure la validation. Par exemple, la classe UnicodeEncoding contrôle tous les substituts afin de vérifier qu'ils constituent des paires de substitution valides. (Une paire de substitution se compose d'un caractère avec un point de code compris entre U+D800 et U+DBFF, suivi d'un caractère avec un point de code compris entre U+DC00 et U+DFFF.) Une stratégie de secours détermine comment un encodeur gère des caractères non valides ou comment un décodeur gère des octets non valides.

Remarque AttentionAttention

Les classes d'encodage .NET Framework permettent de stocker et de convertir des données caractères.Elles ne doivent pas être utilisées pour stocker des données binaires sous forme de chaîne.En fonction de l'encodage utilisé, la conversion de données binaires au format de chaîne à l'aide des classes d'encodage peut entraîner un comportement inattendu et générer des données inexactes ou endommagées.Pour convertir des données binaires sous forme de chaîne, utilisez la méthode Convert.ToBase64String(Byte[]).

Les applications qui ciblent le common language runtime utilisent des encodeurs pour mapper des représentations de caractères Unicode pris en charge par le common language runtime à d'autres schémas d'encodage. Elles utilisent des décodeurs pour mapper des caractères d'encodages non Unicode au format Unicode.

Cette rubrique se compose des sections suivantes :

Encodages dans le .NET Framework

Toutes les classes d'encodage de caractères du .NET Framework héritent de la classe System.Text.Encoding, classe abstraite qui définit la fonctionnalité commune à tous les encodages de caractères. Pour accéder aux objets d'encodage individuels implémentés dans le .NET Framework, procédez comme suit :

  • Utilisez les propriétés statiques de la classe Encoding, qui retourne des objets qui représentent les encodages de caractères standard disponibles dans le .NET Framework (ASCII, UTF-7, UTF-8, UTF-16 et UTF-32). Par exemple, la propriété Encoding.Unicode retourne un objet UnicodeEncoding. Chaque objet utilise un remplacement de secours pour traiter les chaînes qu'il ne peut pas encoder et les octets qu'il ne peut pas décoder. (Pour plus d'informations, consultez la section Remplacement de secours.)

  • Appelez le constructeur de classe de l'encodage. Les objets pour les encodages ASCII, UTF-7, UTF-8, UTF-16 et UTF-32 peuvent être instanciés de cette façon. Par défaut, chaque objet utilise le remplacement de secours pour traiter les chaînes qu'il ne peut pas encoder et les octets qu'il ne peut pas décoder. À la place, vous pouvez spécifier qu'une exception doit être levée. (Pour plus d'informations, consultez les sections Remplacement de secours et Exception de secours.)

  • Appelez le constructeur Encoding.Encoding(Int32) et passez -lui un entier qui représente l'encodage. Les objets d'encodage standard utilisent le remplacement de secours et les objets d'encodage de pages de codes et de jeu de caractères codés sur deux octets (DBCS) utilisent l'ajustement de secours pour traiter les chaînes qu'ils ne peuvent pas encoder et les octets qu'ils ne peuvent pas décoder. (Pour plus d'informations, consultez la section Ajustement de secours.)

  • Appelez la méthode Encoding.GetEncoding, qui retourne tous les encodages standard, de page de codes ou DBCS disponibles dans le .NET Framework. Les surcharges vous permettent de spécifier un objet de secours pour l'encodeur et le décodeur.

RemarqueRemarque

La norme Unicode assigne un point de code (nombre) et un nom à chaque caractère de chaque script pris en charge.Par exemple, le caractère « A » est représenté par le point de code U+0041 et par le nom « LATIN CAPITAL LETTER A ».Les encodages UTF définissent des moyens d'encoder ce point de code dans une séquence d'un ou plusieurs octets.Un schéma d'encodage Unicode simplifie le développement d'applications mondialisables dans la mesure où il permet de représenter les caractères de n'importe quel jeu de caractères dans un seul encodage.Il n'est plus nécessaire que les développeurs d'applications conservent une trace du schéma d'encodage utilisé pour produire des caractères pour une langue ou un système d'écriture spécifique. De plus, les données peuvent être partagées entre les systèmes à l'échelle mondiale, sans risque d'endommagement.

Le .NET Framework prend en charge trois encodages définis par la norme Unicode : UTF-8, UTF-16 et UTF-32.Pour plus d'informations, consultez la norme Unicode sur la page d'accueil Unicode

Vous pouvez récupérer des informations sur tous les encodages disponibles dans le .NET Framework en appelant la méthode Encoding.GetEncodings. Le .NET Framework prend en charge les systèmes d'encodage de caractères répertoriés dans le tableau suivant.

Encodage

Classe

Description

Avantages et inconvénients

ASCII

[ T:System.Text.ASCIIEncoding ]

Encode une plage de caractères limitée à l'aide des sept bits inférieurs d'un octet.

Ce système d'encodage ne prend en charge que les valeurs de caractères comprises entre U+0000 et U+007F. Dans la plupart des cas, il s'avère donc inadapté pour les applications internationales.

UTF-7

[ T:System.Text.UTF7Encoding ]

Représente les caractères sous la forme de séquences de caractères ASCII 7 bits. Les caractères Unicode non ASCII sont représentés par une séquence d'échappement de caractères ASCII.

UTF-7 prend en charge des protocoles tels que les protocoles de messagerie électronique et de groupes de discussion. Toutefois, ce codage n'est pas particulièrement sécurisé ou fiable. Dans certains cas, la modification d'un bit peut radicalement altérer l'interprétation d'une chaîne UTF-7 entière. Dans d'autres cas, des chaînes UTF-7 différentes peuvent encoder le même texte. Pour les séquences qui incluent des caractères non ASCII, l'encodage Unicode UTF-7 nécessite davantage d'espace que l'encodage UTF-8 et l'encodage/décodage est plus lent. Par conséquent, vous devez, dans la mesure du possible, utiliser UTF-8 plutôt que UTF-7.

UTF-8

[ T:System.Text.UTF8Encoding ]

Représente chaque point de code Unicode sous la forme d'une séquence d'un à quatre octets.

UTF-8 prend en charge les données de 8 bits et fonctionne correctement avec de nombreux systèmes d'exploitation existants. Pour la plage de caractères ASCII, UTF-8 est identique à l'encodage ASCII et permet un jeu de caractères plus étendu. Toutefois, pour les scripts CJC (chinois, japonais, coréen), UTF-8 peut nécessiter trois octets pour chaque caractère. Les tailles de données risquent alors d'être plus importantes qu'avec UTF-16. Remarquez que, parfois, la quantité de données ASCII, par exemple les balises HTML, justifie l'augmentation de la taille de la plage CJC.

UTF-16

[ T:System.Text.UnicodeEncoding ]

Représente chaque point de code Unicode sous la forme d'une séquence d'un ou deux entiers 16 bits. La plupart des caractères Unicode nécessitent un seul point de code UTF-16, excepté les caractères Unicode supplémentaires (U+10000 et supérieur), qui nécessitent deux points de code de substitution UTF-16. L'ordre de primauté des octets de poids faible (little-endian) et l'ordre de primauté des octets de poids fort (big-endian) sont tous deux pris en charge.

L'encodage UTF-16 est utilisé par le common language runtime pour représenter des valeurs Char et String. Il est utilisé par le système d'exploitation Windows pour représenter des valeurs WCHAR.

UTF-32

[ T:System.Text.UTF32Encoding ]

Représente chaque point de code Unicode sous la forme d'un entier 32 bits. L'ordre de primauté des octets de poids faible (little-endian) et l'ordre de primauté des octets de poids fort (big-endian) sont tous deux pris en charge.

L'encodage UTF-32 est utilisé lorsque les applications souhaitent éviter le comportement de point de code de substitution de l'encodage UTF-16 sur les systèmes d'exploitation pour lesquels l'espace encodé est trop important. Les glyphes uniques restitués sur un affichage peuvent encore être encodés avec plusieurs caractères UTF-32.

Encodages ANSI/ISO

Prend en charge diverses pages de codes. Sur les systèmes d'exploitation Windows, les pages de codes sont utilisées pour prendre en charge une langue ou un groupe de langues spécifique. Pour consulter un tableau répertoriant les pages de code prises en charge par le .NET Framework, reportez-vous à la classe Encoding. Vous pouvez récupérer un objet d'encodage pour une page de codes spécifique en appelant la méthode Encoding.GetEncoding(Int32).

Une page de codes contient 256 points de code et commence par zéro. Dans la plupart des pages de codes, les points de code de 0 à 127 représentent le jeu de caractères ASCII et les points de code de 128 à 255 diffèrent considérablement d'une page de codes à l'autre. Par exemple, la page de codes 1252 fournit les caractères pour les systèmes d'écriture latins, dont l'anglais, l'allemand et le français. Les 128 derniers points de code dans la page de codes 1252 contiennent des caractères accentués. La page de codes 1253 fournit les codes de caractères requis par le système d'écriture grec. Les 128 derniers points de code dans la page de codes 1253 contiennent des caractères grecs. Par conséquent, une application basée sur les pages de codes ANSI ne peut pas stocker le grec et l'allemand dans le même flux de code, sauf si elle inclut un identificateur qui précise la page de codes référencée.

Encodages de jeu de caractères codés sur deux octets (DBCS)

Prend en charge les langues qui contiennent plus de 256 caractères, tel que le chinois, le japonais et le coréen. Dans les jeux de caractères DBCS, une paire de points de code (un double octet) représente chaque caractère. La propriété Encoding.IsSingleByte retourne false pour les encodages DBCS. Vous pouvez récupérer un objet d'encodage pour un DBCS spécifique en appelant la méthode Encoding.GetEncoding(Int32).

Dans les jeux de caractères DBCS, une paire de points de code (un double octet) représente chaque caractère. Lors du traitement des données DBCS, le premier octet d'un caractère DBCS (l'octet de tête) est traité en combinaison avec l'octet de fin qui le suit immédiatement. Étant donné qu'une paire de points de code de double octet peut représenter des caractères différents, en fonction de la page de codes, cette méthode ne permet pas encore la combinaison de deux langues, tel que le japonais et le chinois, dans le même flux de données.

Ces encodages vous permettent de travailler avec des caractères Unicode, ainsi qu'avec les encodages les plus couramment utilisés dans les applications héritées. En outre, vous pouvez créer un encodage personnalisé en définissant une classe qui dérive de Encoding et en substituant ses membres.

Sélection d'une Classe Encoding

Si vous avez la possibilité de choisir l'encodage à utiliser par votre application, vous devez utiliser un encodage Unicode, de préférence UTF8Encoding ou UnicodeEncoding. (Le .NET Framework prend également en charge un troisième encodage Unicode UTF32Encoding.)

Si vous envisagez d'utiliser un encodage ASCII (ASCIIEncoding), sélectionnez de préférence UTF8Encoding. Les deux encodages sont identiques pour le jeu de caractères ASCII, mais UTF8Encoding offre les avantages suivants :

  • Il peut représenter chaque caractère Unicode, alors que ASCIIEncoding prend uniquement en charge les valeurs des caractères Unicode compris entre U+0000 et U+007F.

  • Il propose la fonctionnalité de détection d'erreurs et fournit une meilleure sécurité.

  • Il a été mis au point pour être le plus rapide possible. Il doit donc être plus rapide que tout autre encodage. Même pour le contenu qui est entièrement ASCII, les opérations exécutées avec le UTF8Encoding sont plus rapides que les opérations exécutées avec le ASCIIEncoding.

Les développeurs doivent utiliser ASCIIEncoding uniquement pour les applications héritées. Toutefois, même pour les applications héritées, UTF8Encoding peut s'avérer un choix plus judicieux, pour les raisons suivantes (si les paramètres par défaut sont utilisés) :

  • Si votre application possède du contenu qui n'est pas strictement ASCII et que vous l'encodez avec ASCIIEncoding, chaque caractère non ASCII sera encodé sous la forme d'un point d'interrogation (?). Si l'application décode ensuite ces données, les informations sont perdues.

  • Si votre application possède un contenu qui n'est pas strictement ASCII et l'encode avec UTF8Encoding, le résultat paraît inintelligible s'il est interprété comme ASCII. Toutefois, si l'application utilise ensuite un décodeur UTF-8 pour décoder ces données, les données exécutent la boucle avec succès.

Dans une application Web, les caractères envoyés au client en réponse à une requête Web doivent refléter l'encodage utilisé sur le client. Dans la plupart des cas, vous devez attribuer à la propriété HttpResponse.ContentEncoding la valeur retournée par la propriété HttpRequest.ContentEncoding pour afficher le texte selon l'encodage attendu par l'utilisateur.

Utilisation d'un objet d'encodage

Un encodeur convertit une chaîne de caractères (le plus souvent, des caractères Unicode) en son équivalent numérique (en octets). Par exemple, vous pouvez utiliser un encodeur ASCII pour convertir des caractères Unicode en ASCII afin qu'ils puissent être affichés dans la console. Pour effectuer la conversion, vous appelez la méthode Encoding.GetBytes. Si vous voulez déterminer le nombre d'octets nécessaires pour stocker des caractères encodés avant d'effectuer l'encodage, vous pouvez appeler la méthode GetByteCount.

L'exemple suivant utilise un tableau d'octets unique pour encoder des chaînes dans deux opérations distinctes. Il gère un index qui indique la position de départ dans le tableau d'octets pour l'ensemble suivant d'octets encodés en ASCII. Il appelle la méthode ASCIIEncoding.GetByteCount(String) afin de garantir que le tableau d'octets est assez grand pour recevoir à la chaîne encodée. Il appelle ensuite la méthode ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) pour encoder les caractères dans la chaîne.

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 décodeur convertit un tableau d'octets qui reflète un encodage de caractères spécifique en un jeu de caractères, dans un tableau de caractères ou dans une chaîne. Pour décoder un tableau d'octets en tableau de caractères, vous appelez la méthode Encoding.GetChars. Pour décoder un tableau d'octets en chaîne, vous appelez la méthode GetString. Si vous voulez déterminer le nombre de caractères nécessaires pour stocker les octets décodés avant d'effectuer le décodage, vous pouvez appeler la méthode GetCharCount.

L'exemple suivant encode trois chaînes, puis les décode en tableau de caractères unique. Il gère un index qui indique la position de départ dans le tableau de caractères pour l'ensemble suivant de caractères décodés. Il appelle la méthode GetCharCount pour garantir que le tableau de caractères est assez grand pour recevoir tous les caractères décodés. Il appelle ensuite la méthode ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) pour décoder le tableau d'octets.

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.

Les méthodes d'encodage et de décodage d'une classe dérivée de Encoding sont conçues pour fonctionner sur un groupe de données complet. Toutes les données à encoder ou à décoder sont fournies dans un seul appel de méthode. Toutefois, dans certains cas, les données sont disponibles dans un flux et les données à encoder ou à décoder peuvent être disponibles uniquement via des opérations de lecture distinctes. L'opération d'encodage ou de décodage doit ainsi se souvenir de tout état enregistré depuis l'invocation précédente. Les méthodes des classes dérivées de Encoder et de Decoder peuvent effectuer des opérations d'encodage et de décodage qui couvrent plusieurs appels de méthode.

Un objet Encoder pour un encodage particulier est fourni dans la propriété Encoding.GetEncoder de cet encodage. Un objet Decoder pour un encodage particulier est fourni dans la propriété Encoding.GetDecoder de cet encodage. Pour les opérations de décodage, notez que les classes dérivées de Decoder comprennent une méthode Decoder.GetChars, mais qu'elles ne comprennent pas de méthode correspondant à Encoding.GetString.

L'exemple suivant illustre la différence entre l'utilisation de la méthode Encoding.GetChars et de la méthode Decoder.GetChars pour décoder un tableau d'octets Unicode. L'exemple encode une chaîne qui contient des caractères Unicode dans un fichier, puis utilise les deux méthodes de décodage pour les décoder en dix octets à la fois. Étant donné qu'une paire de substitution se produit au dixième et au onzième octet, elle est décodée dans des appels de méthode distincts. Comme l'indique la sortie, la méthode Encoding.GetChars ne peut pas décoder correctement les octets et, à la place, les remplace par U+FFFD (CARACTÈRE DE REMPLACEMENT). En revanche, la méthode Decoder.GetChars peut décoder correctement le tableau d'octets pour obtenir la chaîne d'origine.

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

Choix d'une stratégie de secours

Lorsqu'une méthode essaie d'encoder ou de décoder un caractère mais qu'aucun mappage n'existe, elle doit implémenter une stratégie de secours qui détermine comment gérer l'échec du mappage. Il existe trois types de stratégies de secours :

  • Ajustement de secours

  • Remplacement de secours

  • Exception de secours

Remarque importanteImportant

Dans les opérations d'encodage, les problèmes les plus courants surviennent lorsqu'un caractère Unicode ne peut pas être mappé à un encodage de page de codes particulier.Dans les opérations de décodage, les problèmes les plus courants surviennent lorsque des séquences d'octets valides ne peuvent pas être traduites en caractères Unicode valides.C'est pourquoi vous devez savoir quelle stratégie de secours utilise un objet d'encodage particulier.Dans la mesure du possible, vous devez spécifier la stratégie de secours utilisée par un objet d'encodage lorsque vous instanciez l'objet.

Ajustement de secours

Lorsqu'un caractère n'a pas de correspondance exacte dans l'encodage cible, l'encodeur peut essayer de le mapper à un caractère similaire. (L'ajustement de secours est principalement un problème d'encodage et plutôt que de décodage. Très peu de pages de codes contiennent des caractères qui ne peuvent pas être mappés au format Unicode.) L'ajustement de secours est le secours par défaut pour les encodages de page de codes et de jeu de caractères codés sur deux octets qui sont récupérés par les surcharges Encoding.GetEncoding(Int32) et Encoding.GetEncoding(String).

RemarqueRemarque

Dans la théorie, les classes d'encodage Unicode fournies dans le .NET Framework (UTF8Encoding, UnicodeEncoding et UTF32Encoding) prennent en charge chaque caractère de chaque jeu de caractères, afin qu'ils puissent être utilisés pour éliminer les problèmes d'ajustement de secours.

Les stratégies d'ajustement varient pour les différentes pages de codes et ne sont pas documentées en détail. Par exemple, pour certaines pages de codes, les caractères latins à pleine chasse sont mappés aux caractères latins à demi-chasse les plus communs. Pour d'autres pages de codes, ce mappage n'est pas fait. Certains caractères dans certains encodages ne peuvent pas être ajustés, même avec une stratégie d'ajustement agressive. Par exemple, un idéogramme chinois n'a aucun mappage raisonnable pour la page de codes 1252. Dans ce cas, une chaîne de remplacement est utilisée. Par défaut, cette chaîne correspond à un simple POINT D'INTERROGATION (U+003F).

L'exemple suivant utilise la page de codes 1252 (la page de codes Windows pour des langues d'Europe de l'Ouest) pour illustrer le mappage ajusté et ses inconvénients. La méthode Encoding.GetEncoding(Int32) est utilisée pour récupérer un objet d'encodage pour la page de codes 1252. Par défaut, elle utilise un mappage ajusté pour les caractères Unicode qu'elle ne prend pas en charge. L'exemple instancie une chaîne contenant trois caractères non ASCII - la LETTRE MAJUSCULE LATINE CERCLÉE (U+24C8), L'EXPOSANT CINQ (U+2075) et l'INFINITÉ (U+221E) - séparés par des espaces. Comme le montre la sortie de l'exemple, lorsque la chaîne est encodée, les trois caractères autres qu'un espace d'origine sont remplacés par un POINT D'INTERROGATION (U+003F), le CHIFFRE CINQ (U+0035) et le CHIFFRE HUIT (U+0038). Le CHIFFRE HUIT est un substitut particulièrement médiocre pour le caractère d'INFINITÉ non pris en charge et le POINT D'INTERROGATION indique qu'aucun mappage n'est disponible pour le caractère d'origine.

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

Le mappage ajusté est le comportement par défaut d'un objet Encoding qui encode les données Unicode en données de page de codes. Des applications héritées comptent sur ce comportement. Toutefois, la plupart des nouvelles applications doivent éviter le comportement d'ajustement, pour des raisons de sécurité. Par exemple, les applications ne doivent pas appliquer un encodage ajusté à un nom de domaine.

RemarqueRemarque

Vous pouvez également implémenter un mappage d'ajustement de secours personnalisé pour un encodage.Pour plus d'informations, consultez la section Implémentation d'une stratégie de secours personnalisée.

Si l'ajustement de secours est le comportement par défaut pour un objet d'encodage, vous pouvez choisir une autre stratégie de secours lorsque vous récupérez un objet Encoding en appelant les surcharges Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) ou Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). La section suivante présente un exemple dans lequel chaque caractère qui ne peut pas être mappé à la page de codes 1252 est remplacé par un astérisque (*).

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

Remplacement de secours

Lorsqu'un caractère n'a pas de correspondance exacte dans le modèle cible, mais qu'il ne peut être mappé à aucun caractère approprié, l'application peut spécifier un caractère ou une chaîne de remplacement. Il s'agit du comportement par défaut du décodeur Unicode, qui remplace toutes les séquences de deux octets qu'il ne peut pas décoder par le CARACTÈRE DE REMPLACEMENT (U+FFFD). Il s'agit également du comportement pas défaut de la classe ASCIIEncoding, qui remplace chaque caractère qu'il ne peut pas encoder ou décoder par un point d'interrogation. L'exemple suivant illustre le remplacement de caractères pour la chaîne Unicode de l'exemple précédent. Comme l'indique la sortie, chaque caractère qui ne peut pas être décodée en une valeur d'octets ASCII est remplacée par 0x3F, qui est le code ASCII pour un point d'interrogation.

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

Le .NET Framework inclut les classes EncoderReplacementFallback et DecoderReplacementFallback, qui remplacent une chaîne de remplacement si un caractère ne correspond pas exactement dans une opération d'encodage ou de décodage. Par défaut, cette chaîne de remplacement est un point d'interrogation, mais vous pouvez appeler une surcharge de constructeur de classe pour sélectionner une chaîne différente. En général, la chaîne de remplacement est un caractère unique, mais ce n'est pas une obligation. L'exemple suivant modifie le comportement de l'encodeur de la page de codes 1252 en instanciant un objet EncoderReplacementFallback qui utilise un astérisque (*) comme chaîne de remplacement.

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
RemarqueRemarque

Vous pouvez également implémenter une classe de remplacement pour un encodage.Pour plus d'informations, consultez la section Implémentation d'une stratégie de secours personnalisée.

En plus du POINT D'INTERROGATION (U+003F), le CARACTÈRE DE REMPLACEMENT (U+FFFD) est couramment utilisé comme chaîne de remplacement, en particulier lorsque des séquences d'octets de décodage ne peuvent pas être correctement traduites en caractères Unicode. Toutefois, vous êtes libre de sélectionner n'importe quelle chaîne de remplacement, qui peut contenir plusieurs caractères.

Exception de secours

Au lieu de fournir un ajustement de secours ou une chaîne de remplacement, un encodeur peut lever une exception EncoderFallbackException s'il ne peut pas encoder un jeu de caractères et un décodeur peut lever une exception DecoderFallbackException s'il ne peut pas décoder un tableau d'octets. Pour lever une exception dans des opérations d'encodage et de décodage, vous fournissez respectivement un objet EncoderExceptionFallback et un objet DecoderExceptionFallback à la méthode Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). L'exemple suivant illustre l'exception de secours avec 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.
RemarqueRemarque

Vous pouvez également implémenter un gestionnaire d'exceptions personnalisées pour une opération d'encodage.Pour plus d'informations, consultez la section Implémentation d'une stratégie de secours personnalisée.

Les objets EncoderFallbackException et DecoderFallbackException fournissent les informations suivantes concernant la condition qui a provoqué l'exception :

Bien que les objets EncoderFallbackException et DecoderFallbackException fournissent des informations de diagnostic adéquates sur l'exception, ils ne permettent pas d'accéder au tampon d'encodage ou de décodage. Par conséquent, ils ne permettent pas de remplacer ou de corriger des données invalides via la méthode d'encodage ou de décodage.

Implémentation d'une stratégie de secours personnalisée

En plus du mappage ajusté implémenté en interne par les pages de codes, le .NET Framework inclut les classes suivantes pour implémenter une stratégie de secours :

En outre, vous pouvez implémenter une solution personnalisée qui utilise l'ajustement de secours, le remplacement de secours ou l'exception de secours, en procédant comme suit :

  1. Dérivez une classe de EncoderFallback pour les opérations d'encodage et une classe de DecoderFallback pour les opérations de décodage.

  2. Dérivez une classe de EncoderFallbackBuffer pour les opérations d'encodage et une classe de DecoderFallbackBuffer pour les opérations de décodage.

  3. Pour l'exception de secours, si les classes EncoderFallbackException et DecoderFallbackException prédéfinies ne répondent pas à vos besoins, dérivez une classe d'un objet d'exception, tel que Exception ou ArgumentException.

Dérivation de EncoderFallback ou DecoderFallback

Pour implémenter une solution de secours personnalisée, vous devez créer une classe qui hérite de EncoderFallback pour les opérations d'encodage et de DecoderFallback pour les opérations de décodage. Les instances de ces classes sont passées à la méthode Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) et servent d'intermédiaire entre la classe d'encodage et l'implémentation de secours.

Lorsque vous créez une solution de secours personnalisée pour un encodeur ou un décodeur, vous devez implémenter les membres suivants :

Dérivation de EncoderFallbackBuffer ou de DecoderFallbackBuffer

Pour implémenter une solution de secours personnalisée, vous devez également créer une classe qui hérite de EncoderFallbackBuffer pour les opérations d'encodage et de DecoderFallbackBuffer pour les opérations de décodage. Les instances de ces classes sont retournées par la méthode CreateFallbackBuffer des classes EncoderFallback et DecoderFallback. La méthode EncoderFallback.CreateFallbackBuffer est appelée par l'encodeur lorsqu'il rencontre le premier caractère qu'il ne peut pas encoder. La méthode DecoderFallback.CreateFallbackBuffer est appelée par le décodeur lorsqu'il rencontre un ou plusieurs octets qu'il ne peut pas décoder. Les classes EncoderFallbackBuffer et DecoderFallbackBuffer fournissent l'implémentation de secours. Chaque instance représente une mémoire tampon qui contient les caractères de secours qui remplaceront le caractère qui ne peut pas être encodé ou la séquence d'octets qui ne peut pas être décodée.

Lorsque vous créez une solution de secours personnalisée pour un encodeur ou un décodeur, vous devez implémenter les membres suivants :

  • La méthode EncoderFallbackBuffer.Fallback() ou DecoderFallbackBuffer.Fallback. EncoderFallbackBuffer.Fallback() est appelé par l'encodeur pour fournir à la mémoire tampon de secours des informations concernant le caractère qu'il ne parvient pas à encoder. Étant donné que le caractère à encoder peut être une paire de substitution, cette méthode est surchargée. Le caractère à encoder et son index dans la chaîne sont passés à la surcharge. Le substitut étendu et le substitut faible, ainsi que leur index dans la chaîne, sont passés à la deuxième surcharge. La méthode DecoderFallbackBuffer.Fallback est appelée par le décodeur pour fournir à la mémoire tampon de secours des informations sur les octets qu'il ne peut pas décoder. Un tableau d'octets qui ne peut pas être décodé et l'index du premier octet sont passés à cette même méthode. La méthode de secours doit retourner la valeur true si la mémoire tampon de secours peut fournir un ou plusieurs caractères d'ajustement ou de remplacement. Si ce n'est pas le cas, elle doit retourner la valeur false. Dans le cas d'une exception de secours, la méthode de secours doit lever une exception.

  • La méthode EncoderFallbackBuffer.GetNextChar() ou la méthode DecoderFallbackBuffer.GetNextChar(), qui est appelée à plusieurs reprises par l'encodeur ou le décodeur pour obtenir le caractère suivant de la mémoire tampon de secours. Lorsque tous les caractères de secours ont été retournés, la méthode doit retourner U+0000.

  • La propriété EncoderFallbackBuffer.Remaining ou la propriété DecoderFallbackBuffer.Remaining, qui retourne le nombre de caractères restants dans la mémoire tampon de secours.

  • La méthode EncoderFallbackBuffer.MovePrevious() ou la méthode DecoderFallbackBuffer.MovePrevious(), qui déplace la position actuelle dans la mémoire tampon de secours au caractère précédent.

  • La méthode EncoderFallbackBuffer.Reset() ou la méthode DecoderFallbackBuffer.Reset(), qui réinitialise la mémoire tampon de secours.

Si l'implémentation de secours est un ajustement de secours ou un remplacement de secours, les classes dérivées de EncoderFallbackBuffer et de DecoderFallbackBuffer conservent également deux champs d'instance privés : le nombre exact de caractères dans la mémoire tampon et l'index du caractère suivant dans la mémoire tampon à retourner.

Exemple d'un EncoderFallback

Dans un exemple précédent, le remplacement de secours a été utilisé pour remplacer les caractères Unicode qui ne correspondaient pas aux caractères ASCII par un astérisque (*). L'exemple suivant utilise une implémentation d'ajustement de secours personnalisé pour fournir un meilleur mappage des caractères ASCII.

Le code suivant définit une classe nommée CustomMapper dérivée de EncoderFallback pour gérer le mappage ajusté des caractères non ASCII. La méthode CreateFallbackBuffer retourne un objet CustomMapperFallbackBuffer, qui fournit l'implémentation EncoderFallbackBuffer. La classe CustomMapper utilise un objet Dictionary<TKey, TValue> pour stocker les mappages des caractères Unicode (la valeur de clé) et leurs caractères 8 bits correspondants (qui sont stockés dans deux octets consécutifs dans un entier 64 bits) non pris en charge. Pour que ce mappage soit disponible dans la mémoire tampon de secours CustomMapper, l'instance est passée comme paramètre CustomMapperFallbackBuffer au constructeur de classe. Étant donné que le plus long mappage est la chaîne « INF » pour le caractère Unicode U+221E, la propriété MaxCharCount retourne 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; }
   } 
}

Le code suivant définit la classe CustomMapperFallbackBuffer, qui est dérivée de EncoderFallbackBuffer Le dictionnaire qui contient les mappages ajustés et qui est défini dans l'instance CustomMapper est disponible à partir de son constructeur de classe. La méthode Fallback retourne true si un caractère Unicode qui ne peut pas être décodé par l'encodeur ASCII est défini dans le dictionnaire de mappage. Si ce n'est pas le cas, elle retourne false. Pour chaque secours, la variable privée count indique le nombre de caractères qui restent à retourner et la variable index privée indique la position dans la mémoire tampon de chaîne, charsToReturn, du caractère suivant à retourner.

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

Le code suivant instancie alors l'objet CustomMapper et passe une instance de cet objet à la méthode Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). Le résultat indique que l'implémentation de l'ajustement de secours gère correctement les trois caractères non ASCII dans la chaîne d'origine.

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

Voir aussi

Référence

Encoder

Decoder

DecoderFallback

Encoding

EncoderFallback

Autres ressources

Encodage et localisation

Historique des modifications

Date

Historique

Motif

Octobre 2010

Nombreuses modifications.

Améliorations apportées aux informations.