Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article explique comment utiliser les classes que .NET fournit pour l’encodage et le décodage de texte à l’aide de différents schémas d’encodage. Les instructions supposent que vous avez lu Introduction à l’encodage des caractères dans .NET.
Encodeurs et décodeurs
.NET fournit des classes d’encodage qui encodent et décodent du texte à l’aide de différents systèmes d’encodage. Par exemple, la UTF8Encoding classe décrit les règles d’encodage vers et de décodage à partir de UTF-8. .NET utilise l’encodage UTF-16 (représenté par la UnicodeEncoding classe) pour string les instances. Les encodeurs et les décodeurs sont disponibles pour d’autres schémas d’encodage.
L’encodage et le décodage peuvent également inclure la validation. Par exemple, la UnicodeEncoding classe vérifie toutes les char instances de la plage de substitution pour s’assurer qu’elles sont en paires de substitution valides. Une stratégie de secours détermine comment un encodeur gère les caractères non valides ou comment un décodeur gère les octets non valides.
Avertissement
Les classes d’encodage .NET permettent de stocker et de convertir des données de caractères. Ils ne doivent pas être utilisés pour stocker des données binaires sous forme de chaîne. Selon l’encodage utilisé, la conversion de données binaires en format chaîne avec les classes d’encodage peut introduire un comportement inattendu et produire des données inexactes ou endommagées. Pour convertir des données binaires en forme de chaîne, utilisez la Convert.ToBase64String méthode.
Toutes les classes d’encodage de caractères dans .NET héritent de la System.Text.Encoding classe, qui est une classe abstraite qui définit les fonctionnalités communes à tous les encodages de caractères. Pour accéder aux objets d’encodage individuels implémentés dans .NET, procédez comme suit :
Utilisez les propriétés statiques de la Encoding classe, qui retournent des objets qui représentent les encodages de caractères standard disponibles dans .NET (ASCII, UTF-7, UTF-8, UTF-16 et UTF-32). Par exemple, la Encoding.Unicode propriété retourne un UnicodeEncoding objet. Chaque objet utilise le remplacement de secours pour gérer les chaînes qu’il ne peut pas encoder et les octets qu’il ne peut pas décoder. Pour plus d’informations, consultez l'alternative de remplacement.
Appelez le constructeur de classe de l’encodage. Les objets des 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 la substitution de secours pour gérer les chaînes qu'il ne peut pas encoder et les octets qu'il ne peut pas décoder, mais vous pouvez spécifier qu’une exception doit être levée à la place. Pour plus d’informations, consultez Substitution de remplacement et Recours d’exception.
Appelez le Encoding(Int32) constructeur et transmettez-lui un entier qui représente l’encodage. Les objets d’encodage standard utilisent le secours de remplacement, et les pages de codes ainsi que les objets d’encodage DBCS (Jeu de caractères à double octets) utilisent un secours adapté pour gérer 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 solution de secours la mieux adaptée.
Appelez la Encoding.GetEncoding méthode, qui retourne tout encodage standard, page de codes ou DBCS disponible dans .NET. Les surcharges vous permettent de spécifier un objet de secours pour l’encodeur et le décodeur.
Vous pouvez récupérer des informations sur tous les encodages disponibles dans .NET en appelant la Encoding.GetEncodings méthode. .NET prend en charge les schémas d’encodage de caractères répertoriés dans le tableau suivant.
| Classe d’encodage | Description |
|---|---|
| ASCII | Encode une plage limitée de caractères à l’aide des sept bits inférieurs d’un octet. Étant donné que cet encodage prend uniquement en charge les valeurs de caractères à partir de U+0000U+007F, dans la plupart des cas, il est insuffisant pour les applications internationalisées. |
| UTF-7 | Représente des caractères sous 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 les protocoles tels que le courrier électronique et le groupe de news. Toutefois, UTF-7 n’est pas particulièrement sécurisé ou robuste. Dans certains cas, la modification d’un bit peut modifier radicalement l’interprétation d’une chaîne UTF-7 entière. Dans d’autres cas, différentes chaînes UTF-7 peuvent encoder le même texte. Pour les séquences qui incluent des caractères non ASCII, UTF-7 nécessite plus d’espace que UTF-8, et l’encodage/décodage est plus lent. Par conséquent, vous devez utiliser UTF-8 au lieu de UTF-7 si possible. |
| UTF-8 | Représente chaque point de code Unicode sous la forme d’une séquence d’un à quatre octets. UTF-8 prend en charge les tailles de données 8 bits et fonctionne correctement avec de nombreux systèmes d’exploitation existants. Pour la plage ASCII de caractères, UTF-8 est identique à l’encodage ASCII et permet un ensemble plus large de caractères. Toutefois, pour les scripts chinois-Japanese-Korean (CJK), UTF-8 peut nécessiter trois octets pour chaque caractère et peut entraîner des tailles de données supérieures à UTF-16. Parfois, la quantité de données ASCII, telles que les balises HTML, justifie l’augmentation de la taille de la plage CJK. |
| UTF-16 | Représente chaque point de code Unicode sous la forme d’une séquence d’un ou deux entiers 16 bits. Les caractères Unicode les plus courants ne nécessitent qu’un seul point de code UTF-16, bien que les caractères supplémentaires Unicode (U+10000 et supérieur) nécessitent deux points de code de substitution UTF-16. Les ordres d'octets little-endian et big-endian sont pris en charge. L’encodage UTF-16 est utilisé par le Common Language Runtime pour représenter Char et String valeurs, et il est utilisé par le système d’exploitation Windows pour représenter les WCHAR valeurs. |
| UTF-32 | Représente chaque point de code Unicode sous la forme d’un entier 32 bits. Les ordres d'octets little-endian et big-endian sont pris en charge. L’encodage UTF-32 est utilisé lorsque les applications souhaitent éviter le comportement du 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 rendus sur un affichage peuvent toujours être encodés avec plusieurs caractères UTF-32. |
| Encodage ANSI/ISO | Fournit la prise en charge de différentes pages de code. Sur les systèmes d’exploitation Windows, les pages de codes sont utilisées pour prendre en charge une langue ou un groupe spécifique de langues. Pour obtenir un tableau qui répertorie les pages de code prises en charge par .NET, consultez la Encoding classe. Vous pouvez récupérer un objet d’encodage pour une page de codes particulière en appelant la Encoding.GetEncoding(Int32) méthode. Une page de codes contient 256 points de code et est basée sur zéro. Dans la plupart des pages de codes, les points de code 0 à 127 représentent le jeu de caractères ASCII et les points de code 128 à 255 diffèrent considérablement entre les pages de codes. Par exemple, la page de codes 1252 fournit les caractères des systèmes d’écriture latine, y compris l’anglais, l’allemand et le français. Les 128 derniers points de code de la page de codes 1252 contiennent les caractères d’accentuation. La page de codes 1253 fournit des codes de caractères requis dans le système d’écriture grec. Les 128 derniers points de code de la page de codes 1253 contiennent les caractères grecs. Par conséquent, une application qui s’appuie sur des pages de codes ANSI ne peut pas stocker le grec et l’allemand dans le même flux de texte, sauf si elle inclut un identificateur qui indique 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, telles que le chinois, le japonais et le coréen, qui contiennent plus de 256 caractères. Dans un DBCS, une paire de points de code (un double octet) représente chaque caractère. La Encoding.IsSingleByte propriété renvoie false pour les encodages DBCS. Vous pouvez récupérer un objet d’encodage pour un DBCS particulier en appelant la Encoding.GetEncoding(Int32) méthode. Lorsqu’une application gère les 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 unique de points de code double octet peut représenter des caractères différents en fonction de la page de codes, ce schéma n’autorise toujours pas la combinaison de deux langues, telles que le japonais et le chinois, dans le même flux de données. |
Ces encodages vous permettent d’utiliser des caractères Unicode, ainsi que des 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 redéfinissant ses membres.
Prise en charge de l’encodage .NET Core
Par défaut, .NET Core ne met pas à disposition d’encodages de page de codes autres que la page de codes 28591 et les encodages Unicode, tels que UTF-8 et UTF-16. Toutefois, vous pouvez ajouter les encodages de page de codes trouvés dans les applications Windows standard qui ciblent .NET à votre application. Pour plus d’informations, consultez la CodePagesEncodingProvider rubrique.
Sélection d’une classe d’encodage
Si vous avez la possibilité de choisir l’encodage à utiliser par votre application, vous devez utiliser un encodage Unicode de préférence ou UTF8EncodingUnicodeEncoding. (.NET prend également en charge un troisième encodage Unicode, UTF32Encoding.)
Si vous envisagez d’utiliser un encodage ASCII (ASCIIEncoding), choisissez UTF8Encoding à la place. Les deux encodages sont identiques pour le jeu de caractères ASCII, mais UTF8Encoding présentent les avantages suivants :
Il peut représenter chaque caractère Unicode, tandis que ASCIIEncoding prend uniquement en charge les valeurs de caractères Unicode comprises entre U+0000 et U+007F.
Il fournit une détection des erreurs et une meilleure sécurité.
Elle a été paramétrée pour être aussi rapide que possible et doit être plus rapide que n’importe quel autre encodage. Même pour le contenu entièrement ASCII, les opérations effectuées avec UTF8Encoding sont plus rapides que les opérations effectuées avec ASCIIEncoding.
Vous devez envisager d’utiliser ASCIIEncoding uniquement pour les applications héritées. Toutefois, même pour les applications héritées, UTF8Encoding peut être un meilleur choix pour les raisons suivantes (en supposant les paramètres par défaut) :
Si votre application a du contenu qui n’est pas strictement ASCII et l’encode avec ASCIIEncoding, chaque caractère non-ASCII encode en tant que point d’interrogation ( ?). Si l’application décode ensuite ces données, les informations sont perdues.
Si votre application a du contenu qui n’est pas strictement ASCII et l’encode UTF8Encoding, le résultat semble 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 effectuent un aller-retour avec succès.
Dans une application web, les caractères envoyés au client en réponse à une demande web doivent refléter l’encodage utilisé sur le client. Dans la plupart des cas, vous devez définir la HttpResponse.ContentEncoding propriété sur la valeur retournée par la HttpRequest.ContentEncoding propriété pour afficher du texte dans l’encodage attendu par l’utilisateur.
Utilisation d’un objet d’encodage
Un encodeur convertit une chaîne de caractères (le plus souvent, caractères Unicode) en son équivalent numérique (octet). Par exemple, vous pouvez utiliser un encodeur ASCII pour convertir des caractères Unicode en ASCII afin qu’ils puissent être affichés sur la console. Pour effectuer la conversion, vous appelez la Encoding.GetBytes méthode. Si vous souhaitez déterminer le nombre d’octets nécessaires pour stocker les caractères encodés avant d’effectuer l’encodage, vous pouvez appeler la GetByteCount méthode.
L’exemple suivant utilise un tableau d’octets unique pour encoder des chaînes dans deux opérations distinctes. Il conserve un index qui indique la position de départ dans le tableau d’octets pour l’ensemble suivant d’octets encodés ASCII. Il appelle la ASCIIEncoding.GetByteCount(String) méthode pour s’assurer que le tableau d’octets est suffisamment grand pour prendre en charge la chaîne encodée. Il appelle ensuite la ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) méthode pour encoder les caractères dans la chaîne.
using System;
using System.Text;
public class Example
{
public static void Main()
{
string[] strings= { "This is the first sentence. ",
"This is the second sentence. " };
Encoding asciiEncoding = Encoding.ASCII;
// Create array of adequate size.
byte[] bytes = new byte[49];
// Create index for current position of array.
int index = 0;
Console.WriteLine("Strings to encode:");
foreach (var stringValue in strings) {
Console.WriteLine($" {stringValue}");
int count = asciiEncoding.GetByteCount(stringValue);
if (count + index >= bytes.Length)
Array.Resize(ref bytes, bytes.Length + 50);
int written = asciiEncoding.GetBytes(stringValue, 0,
stringValue.Length,
bytes, index);
index = index + written;
}
Console.WriteLine("\nEncoded bytes:");
Console.WriteLine($"{ShowByteValues(bytes, index)}");
Console.WriteLine();
// Decode Unicode byte array to a string.
string newString = asciiEncoding.GetString(bytes, 0, index);
Console.WriteLine($"Decoded: {newString}");
}
private static string ShowByteValues(byte[] bytes, int last )
{
string returnString = " ";
for (int ctr = 0; ctr <= last - 1; ctr++) {
if (ctr % 20 == 0)
returnString += "\n ";
returnString += String.Format("{0:X2} ", bytes[ctr]);
}
return returnString;
}
}
// The example displays the following output:
// Strings to encode:
// This is the first sentence.
// This is the second sentence.
//
// Encoded bytes:
//
// 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
// 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
// 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
//
// Decoded: This is the first sentence. This is the second sentence.
Imports System.Text
Module Example
Public Sub Main()
Dim strings() As String = {"This is the first sentence. ",
"This is the second sentence. "}
Dim asciiEncoding As Encoding = Encoding.ASCII
' Create array of adequate size.
Dim bytes(50) As Byte
' Create index for current position of array.
Dim index As Integer = 0
Console.WriteLine("Strings to encode:")
For Each stringValue In strings
Console.WriteLine(" {0}", stringValue)
Dim count As Integer = asciiEncoding.GetByteCount(stringValue)
If count + index >= bytes.Length Then
Array.Resize(bytes, bytes.Length + 50)
End If
Dim written As Integer = asciiEncoding.GetBytes(stringValue, 0,
stringValue.Length,
bytes, index)
index = index + written
Next
Console.WriteLine()
Console.WriteLine("Encoded bytes:")
Console.WriteLine("{0}", ShowByteValues(bytes, index))
Console.WriteLine()
' Decode Unicode byte array to a string.
Dim newString As String = asciiEncoding.GetString(bytes, 0, index)
Console.WriteLine("Decoded: {0}", newString)
End Sub
Private Function ShowByteValues(bytes As Byte(), last As Integer) As String
Dim returnString As String = " "
For ctr As Integer = 0 To last - 1
If ctr Mod 20 = 0 Then returnString += vbCrLf + " "
returnString += String.Format("{0:X2} ", bytes(ctr))
Next
Return returnString
End Function
End Module
' The example displays the following output:
' Strings to encode:
' This is the first sentence.
' This is the second sentence.
'
' Encoded bytes:
'
' 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
' 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
' 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
'
' Decoded: This is the first sentence. This is the second sentence.
Un décodeur convertit un tableau d’octets qui reflète un encodage de caractères particulier en un ensemble de caractères, dans un tableau de caractères ou dans une chaîne. Pour décoder un tableau d’octets dans un tableau de caractères, vous appelez la Encoding.GetChars méthode. Pour décoder un tableau d’octets dans une chaîne, vous appelez la GetString méthode. Si vous souhaitez 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 GetCharCount méthode.
L’exemple suivant encode trois chaînes, puis les décode en un seul tableau de caractères. Il conserve un index qui indique la position de départ dans le tableau de caractères pour le jeu de caractères décodé suivant. Il appelle la GetCharCount méthode pour s’assurer que le tableau de caractères est suffisamment grand pour prendre en charge tous les caractères décodés. Il appelle ensuite la ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) méthode pour décoder le tableau d’octets.
using System;
using System.Text;
public class Example
{
public static void Main()
{
string[] strings = { "This is the first sentence. ",
"This is the second sentence. ",
"This is the third sentence. " };
Encoding asciiEncoding = Encoding.ASCII;
// Array to hold encoded bytes.
byte[] bytes;
// Array to hold decoded characters.
char[] chars = new char[50];
// Create index for current position of character array.
int index = 0;
foreach (var stringValue in strings) {
Console.WriteLine($"String to Encode: {stringValue}");
// Encode the string to a byte array.
bytes = asciiEncoding.GetBytes(stringValue);
// Display the encoded bytes.
Console.Write("Encoded bytes: ");
for (int ctr = 0; ctr < bytes.Length; ctr++)
Console.Write(" {0}{1:X2}",
ctr % 20 == 0 ? Environment.NewLine : "",
bytes[ctr]);
Console.WriteLine();
// Decode the bytes to a single character array.
int count = asciiEncoding.GetCharCount(bytes);
if (count + index >= chars.Length)
Array.Resize(ref chars, chars.Length + 50);
int written = asciiEncoding.GetChars(bytes, 0,
bytes.Length,
chars, index);
index = index + written;
Console.WriteLine();
}
// Instantiate a single string containing the characters.
string decodedString = new string(chars, 0, index - 1);
Console.WriteLine("Decoded string: ");
Console.WriteLine(decodedString);
}
}
// The example displays the following output:
// String to Encode: This is the first sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
// 6E 74 65 6E 63 65 2E 20
//
// String to Encode: This is the second sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
// 65 6E 74 65 6E 63 65 2E 20
//
// String to Encode: This is the third sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
// 6E 74 65 6E 63 65 2E 20
//
// Decoded string:
// This is the first sentence. This is the second sentence. This is the third sentence.
Imports System.Text
Module Example
Public Sub Main()
Dim strings() As String = {"This is the first sentence. ",
"This is the second sentence. ",
"This is the third sentence. "}
Dim asciiEncoding As Encoding = Encoding.ASCII
' Array to hold encoded bytes.
Dim bytes() As Byte
' Array to hold decoded characters.
Dim chars(50) As Char
' Create index for current position of character array.
Dim index As Integer
For Each stringValue In strings
Console.WriteLine("String to Encode: {0}", stringValue)
' Encode the string to a byte array.
bytes = asciiEncoding.GetBytes(stringValue)
' Display the encoded bytes.
Console.Write("Encoded bytes: ")
For ctr As Integer = 0 To bytes.Length - 1
Console.Write(" {0}{1:X2}", If(ctr Mod 20 = 0, vbCrLf, ""),
bytes(ctr))
Next
Console.WriteLine()
' Decode the bytes to a single character array.
Dim count As Integer = asciiEncoding.GetCharCount(bytes)
If count + index >= chars.Length Then
Array.Resize(chars, chars.Length + 50)
End If
Dim written As Integer = asciiEncoding.GetChars(bytes, 0,
bytes.Length,
chars, index)
index = index + written
Console.WriteLine()
Next
' Instantiate a single string containing the characters.
Dim decodedString As New String(chars, 0, index - 1)
Console.WriteLine("Decoded string: ")
Console.WriteLine(decodedString)
End Sub
End Module
' The example displays the following output:
' String to Encode: This is the first sentence.
' Encoded bytes:
' 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
' 6E 74 65 6E 63 65 2E 20
'
' String to Encode: This is the second sentence.
' Encoded bytes:
' 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
' 65 6E 74 65 6E 63 65 2E 20
'
' String to Encode: This is the third sentence.
' Encoded bytes:
' 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
' 6E 74 65 6E 63 65 2E 20
'
' Decoded string:
' This is the first sentence. This is the second sentence. This is the third sentence.
Les méthodes d’encodage et de décodage d’une classe dérivées Encoding sont conçues pour fonctionner sur un ensemble complet de données ; autrement dit, toutes les données à encoder ou décodées 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 à partir d’opérations de lecture distinctes. Cela nécessite l’opération d’encodage ou de décodage pour mémoriser tout état enregistré de son appel précédent. Les méthodes des classes dérivées de Encoder et Decoder sont capables de gérer les opérations d'encodage et de décodage qui s'étendent sur plusieurs appels de méthode.
Un Encoder objet pour un encodage particulier est disponible à partir de la propriété de cet Encoding.GetEncoder() encodage. Un Decoder objet pour un encodage particulier est disponible via sa propriété Encoding.GetDecoder(). Pour les opérations de décodage, notez que les classes dérivées d’une Decoder méthode incluent une Decoder.GetChars méthode, mais qu’elles n’ont pas de méthode qui correspond à Encoding.GetString.
L’exemple suivant illustre la différence entre l’utilisation des méthodes Encoding.GetString et Decoder.GetChars pour le décodage d’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 de dix octets à la fois. Étant donné qu’une paire de substitution se produit dans les dixième et onze octets, elle est décodée dans des appels de méthode distincts. Comme le montre la sortie, la Encoding.GetString méthode ne peut pas décoder correctement les octets et les remplace par U+FFFD (REPLACEMENT CHARACTER). En revanche, la Decoder.GetChars méthode est en mesure de décoder correctement le tableau d’octets pour obtenir la chaîne d’origine.
using System;
using System.IO;
using System.Text;
public class Example
{
public static void Main()
{
// Use default replacement fallback for invalid encoding.
UnicodeEncoding enc = new UnicodeEncoding(true, false, false);
// Define a string with various Unicode characters.
string str1 = "AB YZ 19 \uD800\udc05 \u00e4";
str1 += "Unicode characters. \u00a9 \u010C s \u0062\u0308";
Console.WriteLine("Created original string...\n");
// Convert string to byte array.
byte[] bytes = enc.GetBytes(str1);
FileStream fs = File.Create(@".\characters.bin");
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(bytes);
bw.Close();
// Read bytes from file.
FileStream fsIn = File.OpenRead(@".\characters.bin");
BinaryReader br = new BinaryReader(fsIn);
const int count = 10; // Number of bytes to read at a time.
byte[] bytesRead = new byte[10]; // Buffer (byte array).
int read; // Number of bytes actually read.
string str2 = String.Empty; // Decoded string.
// Try using Encoding object for all operations.
do {
read = br.Read(bytesRead, 0, count);
str2 += enc.GetString(bytesRead, 0, read);
} while (read == count);
br.Close();
Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...");
CompareForEquality(str1, str2);
Console.WriteLine();
// Use Decoder for all operations.
fsIn = File.OpenRead(@".\characters.bin");
br = new BinaryReader(fsIn);
Decoder decoder = enc.GetDecoder();
char[] chars = new char[50];
int index = 0; // Next character to write in array.
int written = 0; // Number of chars written to array.
do {
read = br.Read(bytesRead, 0, count);
if (index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length)
Array.Resize(ref chars, chars.Length + 50);
written = decoder.GetChars(bytesRead, 0, read, chars, index);
index += written;
} while (read == count);
br.Close();
// Instantiate a string with the decoded characters.
string str3 = new String(chars, 0, index);
Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...");
CompareForEquality(str1, str3);
}
private static void CompareForEquality(string original, string decoded)
{
bool result = original.Equals(decoded);
Console.WriteLine($"original = decoded: {original.Equals(decoded, StringComparison.Ordinal)}");
if (! result) {
Console.WriteLine("Code points in original string:");
foreach (var ch in original)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
Console.WriteLine("Code points in decoded string:");
foreach (var ch in decoded)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Created original string...
//
// Decoded string using UnicodeEncoding.GetString()...
// original = decoded: False
// Code points in original string:
// 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F
// 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
// 0020 0073 0020 0062 0308
// Code points in decoded string:
// 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F
// 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
// 0020 0073 0020 0062 0308
//
// Decoded string using UnicodeEncoding.Decoder.GetString()...
// original = decoded: True
Imports System.IO
Imports System.Text
Module Example
Public Sub Main()
' Use default replacement fallback for invalid encoding.
Dim enc As New UnicodeEncoding(True, False, False)
' Define a string with various Unicode characters.
Dim str1 As String = String.Format("AB YZ 19 {0}{1} {2}",
ChrW(&hD800), ChrW(&hDC05), ChrW(&h00e4))
str1 += String.Format("Unicode characters. {0} {1} s {2}{3}",
ChrW(&h00a9), ChrW(&h010C), ChrW(&h0062), ChrW(&h0308))
Console.WriteLine("Created original string...")
Console.WriteLine()
' Convert string to byte array.
Dim bytes() As Byte = enc.GetBytes(str1)
Dim fs As FileStream = File.Create(".\characters.bin")
Dim bw As New BinaryWriter(fs)
bw.Write(bytes)
bw.Close()
' Read bytes from file.
Dim fsIn As FileStream = File.OpenRead(".\characters.bin")
Dim br As New BinaryReader(fsIn)
Const count As Integer = 10 ' Number of bytes to read at a time.
Dim bytesRead(9) As Byte ' Buffer (byte array).
Dim read As Integer ' Number of bytes actually read.
Dim str2 As String = "" ' Decoded string.
' Try using Encoding object for all operations.
Do
read = br.Read(bytesRead, 0, count)
str2 += enc.GetString(bytesRead, 0, read)
Loop While read = count
br.Close()
Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...")
CompareForEquality(str1, str2)
Console.WriteLine()
' Use Decoder for all operations.
fsIn = File.OpenRead(".\characters.bin")
br = New BinaryReader(fsIn)
Dim decoder As Decoder = enc.GetDecoder()
Dim chars(50) As Char
Dim index As Integer = 0 ' Next character to write in array.
Dim written As Integer = 0 ' Number of chars written to array.
Do
read = br.Read(bytesRead, 0, count)
If index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length Then
Array.Resize(chars, chars.Length + 50)
End If
written = decoder.GetChars(bytesRead, 0, read, chars, index)
index += written
Loop While read = count
br.Close()
' Instantiate a string with the decoded characters.
Dim str3 As New String(chars, 0, index)
Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...")
CompareForEquality(str1, str3)
End Sub
Private Sub CompareForEquality(original As String, decoded As String)
Dim result As Boolean = original.Equals(decoded)
Console.WriteLine("original = decoded: {0}",
original.Equals(decoded, StringComparison.Ordinal))
If Not result Then
Console.WriteLine("Code points in original string:")
For Each ch In original
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine("Code points in decoded string:")
For Each ch In decoded
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module
' The example displays the following output:
' Created original string...
'
' Decoded string using UnicodeEncoding.GetString()...
' original = decoded: False
' Code points in original string:
' 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F
' 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
' 0020 0073 0020 0062 0308
' Code points in decoded string:
' 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F
' 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
' 0020 0073 0020 0062 0308
'
' Decoded string using UnicodeEncoding.Decoder.GetString()...
' original = decoded: True
Choix d’une stratégie de secours
Lorsqu’une méthode tente d’encoder ou de décoder un caractère, mais qu’aucun mappage n’existe, il doit implémenter une stratégie de secours qui détermine comment le mappage ayant échoué doit être géré. Il existe trois types de stratégies de secours :
Solution de secours la plus adaptée
Secours de remplacement
Secours d’exception
Important
Les problèmes les plus courants dans les opérations d’encodage se produisent lorsqu’un caractère Unicode ne peut pas être mappé à un encodage de page de codes particulier. Les problèmes les plus courants dans les opérations de décodage se produisent lorsque les séquences d’octets non valides ne peuvent pas être traduites en caractères Unicode valides. Pour ces raisons, 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.
Meilleur 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. (La solution de secours la mieux adaptée est principalement un problème d'encodage plutôt que de décodage. Il existe très peu de pages de codes qui contiennent des caractères qui ne peuvent pas être correctement mappés à Unicode.) La solution de secours la mieux adaptée est la valeur par défaut pour les pages de codes ainsi que pour les encodages de jeux de caractères à double octet récupérés par les surcharges Encoding.GetEncoding(Int32) et Encoding.GetEncoding(String).
Note
En théorie, les classes d’encodage Unicode fournies dans .NET (UTF8Encoding, UnicodeEncoding, et UTF32Encoding) prennent en charge chaque caractère de chaque jeu de caractères, afin de supprimer les problèmes de correspondance approximative.
Les stratégies les mieux adaptées varient selon les différentes pages de code. Par exemple, pour certaines pages de codes, les caractères latins de pleine largeur sont mappés aux caractères latins de demi-largeur les plus courants. Pour les autres pages de codes, ce mappage n’est pas effectué. Même avec une stratégie agressive de meilleur ajustement, il n’existe pas d’ajustement possible pour certains caractères dans certains encodages. Par exemple, un idéogramme chinois n’a aucune correspondance raisonnable possible avec la page de codes 1252. Dans ce cas, une chaîne de remplacement est utilisée. Par défaut, cette chaîne n’est qu’un point d’interrogation unique (U+003F).
Note
Les stratégies les mieux adaptées ne sont pas documentées en détail. Toutefois, plusieurs pages de codes sont documentées sur le site web du Consortium Unicode . Consultez le fichier readme.txt dans ce dossier pour obtenir une description de l’interprétation des fichiers de mappage.
L’exemple suivant utilise la page de codes 1252 (page de codes Windows pour les langues européennes occidentales) pour illustrer le mappage le mieux adapté et ses inconvénients. La Encoding.GetEncoding(Int32) méthode est utilisée pour récupérer un objet d’encodage pour la page de codes 1252. Par défaut, il utilise un mappage le mieux adapté pour les caractères Unicode qu’il ne prend pas en charge. L’exemple instancie une chaîne qui contient trois caractères non ASCII : LETTRE MAJUSCULE LATINE CERCLÉE S (U+24C8), EXPOSANT CINQ (U+2075) et INFINI (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 d’origine autres que l’espace sont remplacés par QUESTION MARK (U+003F), DIGIT FIVE (U+0035) et DIGIT EIGHT (U+0038). DIGIT EIGHT est un remplacement particulièrement médiocre pour le caractère INFINITY non pris en charge, et QUESTION MARK indique qu’aucun mappage n’a été disponible pour le caractère d’origine.
using System;
using System.Text;
public class Example
{
public static void Main()
{
// Get an encoding for code page 1252 (Western Europe character set).
Encoding cp1252 = Encoding.GetEncoding(1252);
// Define and display a string.
string str = "\u24c8 \u2075 \u221e";
Console.WriteLine("Original string: " + str);
Console.Write("Code points in string: ");
foreach (var ch in str)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine("\n");
// Encode a Unicode string.
Byte[] bytes = cp1252.GetBytes(str);
Console.Write("Encoded bytes: ");
foreach (byte byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine("\n");
// Decode the string.
string str2 = cp1252.GetString(bytes);
Console.WriteLine($"String round-tripped: {str.Equals(str2)}");
if (! str.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
}
}
}
// The example displays the following output:
// Original string: Ⓢ ⁵ ∞
// Code points in string: 24C8 0020 2075 0020 221E
//
// Encoded bytes: 3F 20 35 20 38
//
// String round-tripped: False
// ? 5 8
// 003F 0020 0035 0020 0038
Imports System.Text
Module Example
Public Sub Main()
' Get an encoding for code page 1252 (Western Europe character set).
Dim cp1252 As Encoding = Encoding.GetEncoding(1252)
' Define and display a string.
Dim str As String = String.Format("{0} {1} {2}", ChrW(&h24c8), ChrW(&H2075), ChrW(&h221E))
Console.WriteLine("Original string: " + str)
Console.Write("Code points in string: ")
For Each ch In str
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine()
' Encode a Unicode string.
Dim bytes() As Byte = cp1252.GetBytes(str)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.WriteLine()
Console.WriteLine()
' Decode the string.
Dim str2 As String = cp1252.GetString(bytes)
Console.WriteLine("String round-tripped: {0}", str.Equals(str2))
If Not str.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
End If
End Sub
End Module
' The example displays the following output:
' Original string: Ⓢ ⁵ ∞
' Code points in string: 24C8 0020 2075 0020 221E
'
' Encoded bytes: 3F 20 35 20 38
'
' String round-tripped: False
' ? 5 8
' 003F 0020 0035 0020 0038
Le mappage le mieux adapté est le comportement par défaut d’un Encoding objet qui encode les données Unicode dans les données de page de codes, et il existe des applications héritées qui s’appuient sur ce comportement. Toutefois, la plupart des nouvelles applications doivent éviter le comportement le mieux adapté pour des raisons de sécurité. Par exemple, les applications ne doivent pas passer un nom de domaine via un encodage optimal.
Note
Vous pouvez également implémenter un mappage de repli à meilleur ajustement personnalisé pour un encodage. Pour plus d’informations, consultez la section Implémentation d’une stratégie de secours personnalisée .
Si le secours le mieux adapté est la valeur par défaut d’un objet d’encodage, vous pouvez choisir une autre stratégie de secours en récupérant un objet via les surcharges Encoding ou Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback). La section suivante inclut un exemple qui remplace chaque caractère qui ne peut pas être mappé à la page de codes 1252 par un astérisque (*).
using System;
using System.Text;
public class Example
{
public static void Main()
{
Encoding cp1252r = Encoding.GetEncoding(1252,
new EncoderReplacementFallback("*"),
new DecoderReplacementFallback("*"));
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
byte[] bytes = cp1252r.GetBytes(str1);
string str2 = cp1252r.GetString(bytes);
Console.WriteLine($"Round-trip: {str1.Equals(str2)}");
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
// Round-trip: False
// * * *
// 002A 0020 002A 0020 002A
Imports System.Text
Module Example
Public Sub Main()
Dim cp1252r As Encoding = Encoding.GetEncoding(1252,
New EncoderReplacementFallback("*"),
New DecoderReplacementFallback("*"))
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Dim bytes() As Byte = cp1252r.GetBytes(str1)
Dim str2 As String = cp1252r.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
' Round-trip: False
' * * *
' 002A 0020 002A 0020 002A
Secours de remplacement
Lorsqu’un caractère n’a pas de correspondance exacte dans le schéma cible, mais qu’il n’existe aucun caractère approprié auquel il peut être mappé, 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 toute séquence à deux octets qu’elle ne peut pas décoder par REPLACEMENT_CHARACTER (U+FFFD). Il s’agit également du comportement par défaut de la ASCIIEncoding classe, 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 le montre la sortie, chaque caractère qui ne peut pas être décodé en une valeur d’octet ASCII est remplacé par 0x3F, qui est le code ASCII d’un point d’interrogation.
using System;
using System.Text;
public class Example
{
public static void Main()
{
Encoding enc = Encoding.ASCII;
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine("\n");
// Encode the original string using the ASCII encoder.
byte[] bytes = enc.GetBytes(str1);
Console.Write("Encoded bytes: ");
foreach (var byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine("\n");
// Decode the ASCII bytes.
string str2 = enc.GetString(bytes);
Console.WriteLine($"Round-trip: {str1.Equals(str2)}");
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
//
// Encoded bytes: 3F 20 3F 20 3F
//
// Round-trip: False
// ? ? ?
// 003F 0020 003F 0020 003F
Imports System.Text
Module Example
Public Sub Main()
Dim enc As Encoding = Encoding.Ascii
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine()
' Encode the original string using the ASCII encoder.
Dim bytes() As Byte = enc.GetBytes(str1)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.WriteLine()
Console.WriteLine()
' Decode the ASCII bytes.
Dim str2 As String = enc.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
'
' Encoded bytes: 3F 20 3F 20 3F
'
' Round-trip: False
' ? ? ?
' 003F 0020 003F 0020 003F
.NET inclut les classes EncoderReplacementFallback et DecoderReplacementFallback, qui remplacent par une chaîne de substitution si un caractère ne correspond pas exactement lors d'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 choisir une autre chaîne. En règle générale, la chaîne de remplacement est un caractère unique, même si ce n'est pas obligatoire. L’exemple suivant modifie le comportement de la page de codes 1252 en instanciant un EncoderReplacementFallback objet qui utilise un astérisque (*) comme chaîne de remplacement.
using System;
using System.Text;
public class Example
{
public static void Main()
{
Encoding cp1252r = Encoding.GetEncoding(1252,
new EncoderReplacementFallback("*"),
new DecoderReplacementFallback("*"));
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
byte[] bytes = cp1252r.GetBytes(str1);
string str2 = cp1252r.GetString(bytes);
Console.WriteLine($"Round-trip: {str1.Equals(str2)}");
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
// Round-trip: False
// * * *
// 002A 0020 002A 0020 002A
Imports System.Text
Module Example
Public Sub Main()
Dim cp1252r As Encoding = Encoding.GetEncoding(1252,
New EncoderReplacementFallback("*"),
New DecoderReplacementFallback("*"))
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Dim bytes() As Byte = cp1252r.GetBytes(str1)
Dim str2 As String = cp1252r.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
' Round-trip: False
' * * *
' 002A 0020 002A 0020 002A
Note
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 de QUESTION MARK (U+003F), le CARACTÈRE DE REMPLACEMENT Unicode (U+FFFD) est couramment utilisé comme chaîne de remplacement, en particulier lors du décodage de séquences d’octets qui ne peuvent pas être correctement traduites en caractères Unicode. Toutefois, vous êtes libre de choisir n’importe quelle chaîne de remplacement et peut contenir plusieurs caractères.
Repli en cas d'exception
Au lieu de fournir un secours adapté ou une chaîne de remplacement, un encodeur peut lever un EncoderFallbackException s'il n'est pas capable d'encoder un jeu de caractères, et un décodeur peut lever un DecoderFallbackException s'il n'est pas en mesure de décoder un tableau d'octets. Pour lever une exception dans les opérations d’encodage et de décodage, vous fournissez respectivement un EncoderExceptionFallback objet et un DecoderExceptionFallback objet à la Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) méthode. L’exemple suivant illustre la gestion d'exception avec la classe ASCIIEncoding.
using System;
using System.Text;
public class Example
{
public static void Main()
{
Encoding enc = Encoding.GetEncoding("us-ascii",
new EncoderExceptionFallback(),
new DecoderExceptionFallback());
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine("\n");
// Encode the original string using the ASCII encoder.
byte[] bytes = {};
try {
bytes = enc.GetBytes(str1);
Console.Write("Encoded bytes: ");
foreach (var byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine();
}
catch (EncoderFallbackException e) {
Console.Write("Exception: ");
if (e.IsUnknownSurrogate())
Console.WriteLine($"Unable to encode surrogate pair 0x{Convert.ToUInt16(e.CharUnknownHigh):X4} 0x{Convert.ToUInt16(e.CharUnknownLow):X3} at index {e.Index}.");
else
Console.WriteLine($"Unable to encode 0x{Convert.ToUInt16(e.CharUnknown):X4} at index {e.Index}.");
return;
}
Console.WriteLine();
// Decode the ASCII bytes.
try {
string str2 = enc.GetString(bytes);
Console.WriteLine($"Round-trip: {str1.Equals(str2)}");
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
catch (DecoderFallbackException e) {
Console.Write("Unable to decode byte(s) ");
foreach (byte unknown in e.BytesUnknown)
Console.Write("0x{0:X2} ");
Console.WriteLine($"at index {e.Index}");
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
//
// Exception: Unable to encode 0x24C8 at index 0.
Imports System.Text
Module Example
Public Sub Main()
Dim enc As Encoding = Encoding.GetEncoding("us-ascii",
New EncoderExceptionFallback(),
New DecoderExceptionFallback())
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine()
' Encode the original string using the ASCII encoder.
Dim bytes() As Byte = {}
Try
bytes = enc.GetBytes(str1)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.WriteLine()
Catch e As EncoderFallbackException
Console.Write("Exception: ")
If e.IsUnknownSurrogate() Then
Console.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.",
Convert.ToUInt16(e.CharUnknownHigh),
Convert.ToUInt16(e.CharUnknownLow),
e.Index)
Else
Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.",
Convert.ToUInt16(e.CharUnknown),
e.Index)
End If
Exit Sub
End Try
Console.WriteLine()
' Decode the ASCII bytes.
Try
Dim str2 As String = enc.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
Catch e As DecoderFallbackException
Console.Write("Unable to decode byte(s) ")
For Each unknown As Byte In e.BytesUnknown
Console.Write("0x{0:X2} ")
Next
Console.WriteLine("at index {0}", e.Index)
End Try
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
'
' Exception: Unable to encode 0x24C8 at index 0.
Note
Vous pouvez également implémenter un gestionnaire d’exceptions personnalisé pour une opération d’encodage. Pour plus d’informations, consultez la section Implémentation d’une stratégie de secours personnalisée .
Les EncoderFallbackException objets et DecoderFallbackException fournissent les informations suivantes sur la condition qui a provoqué l’exception :
L’objet EncoderFallbackException inclut une IsUnknownSurrogate méthode, qui indique si le caractère ou les caractères qui ne peuvent pas être encodés représentent une paire de substitution inconnue (auquel cas, la méthode renvoie
true) ou un caractère unique inconnu (auquel cas, la méthode renvoiefalse). Les caractères de la paire de substitution sont disponibles à partir des propriétés EncoderFallbackException.CharUnknownHigh et EncoderFallbackException.CharUnknownLow. Le caractère unique inconnu est disponible dans la propriété EncoderFallbackException.CharUnknown. La EncoderFallbackException.Index propriété indique la position dans la chaîne à laquelle le premier caractère qui n’a pas pu être codé a été trouvé.L’objet DecoderFallbackException inclut une BytesUnknown propriété qui retourne un tableau d’octets qui ne peut pas être décodé. La DecoderFallbackException.Index propriété indique la position de départ des octets inconnus.
Bien que EncoderFallbackException et DecoderFallbackException objets fournissent des informations de diagnostic suffisantes sur l’exception, ils n'offrent pas l’accès au tampon de codage ou de décodage. Par conséquent, ils n’autorisent pas le remplacement ou la correction des données non valides dans la méthode d’encodage ou de décodage.
Implémentation d’une stratégie de secours personnalisée
Outre le mappage le mieux adapté implémenté en interne par les pages de code, .NET inclut les classes suivantes pour implémenter une stratégie de secours :
Utilisez EncoderReplacementFallback et EncoderReplacementFallbackBuffer pour remplacer les caractères dans les opérations d’encodage.
Utilisez DecoderReplacementFallback et DecoderReplacementFallbackBuffer pour remplacer les caractères dans les opérations de décodage.
Utilisez EncoderExceptionFallback et EncoderExceptionFallbackBuffer pour lever un EncoderFallbackException lorsqu’un caractère ne peut pas être encodé.
Utilisez DecoderExceptionFallback et DecoderExceptionFallbackBuffer pour lever un DecoderFallbackException lorsqu’un caractère ne peut pas être décodé.
En outre, vous pouvez implémenter une solution personnalisée qui utilise le mécanisme de secours le mieux adapté, le mécanisme de remplacement ou le mécanisme d’exception, en procédant comme suit :
Dérivez une classe à partir de EncoderFallback pour les opérations d’encodage, et à partir de DecoderFallback pour les opérations de décodage.
Dérivez une classe à partir de EncoderFallbackBuffer pour les opérations d’encodage, et à partir de DecoderFallbackBuffer pour les opérations de décodage.
Pour le repli d'exception, si les classes prédéfinies EncoderFallbackException et DecoderFallbackException ne répondent pas à vos besoins, dérivez une classe à partir 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 Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) méthode 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 :
La propriété EncoderFallback.MaxCharCount ou DecoderFallback.MaxCharCount, qui retourne le nombre maximal de caractères que la solution de secours la plus adaptée, le remplacement ou l’exception peut utiliser pour remplacer un seul caractère. Pour un fallback d’exception personnalisée, sa valeur est zéro.
La méthode EncoderFallback.CreateFallbackBuffer ou DecoderFallback.CreateFallbackBuffer, qui retourne votre implémentation personnalisée de EncoderFallbackBuffer ou DecoderFallbackBuffer. La méthode est appelée par l’encodeur lorsqu’il rencontre le premier caractère qu’il ne parvient pas à encoder correctement, ou par le décodeur lorsqu’il rencontre le premier octet qu’il ne parvient pas à décoder correctement.
Dérivation de EncoderFallbackBuffer ou 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 CreateFallbackBuffer méthode et les EncoderFallbackDecoderFallback classes. La EncoderFallback.CreateFallbackBuffer méthode est appelée par l’encodeur lorsqu’elle rencontre le premier caractère qu’il n’est pas en mesure d’encoder, et la DecoderFallback.CreateFallbackBuffer méthode est appelée par le décodeur lorsqu’elle rencontre un ou plusieurs octets qu’il n’est pas en mesure de 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 remplacent 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 :
EncoderFallbackBuffer.Fallback Ou DecoderFallbackBuffer.Fallback méthode. EncoderFallbackBuffer.Fallback est appelé par l’encodeur pour fournir à la mémoire tampon de secours des informations sur le caractère qu’il ne peut pas encoder. Étant donné que le caractère à encoder peut être une paire de substitution, cette méthode est surchargée. Une surcharge est passée au caractère à encoder et à son index dans la chaîne. La deuxième surcharge est passée le substitut élevé et faible, ainsi que son index dans la chaîne. La DecoderFallbackBuffer.Fallback méthode 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. On passe à cette méthode un tableau d’octets qu'elle ne peut pas décoder, ainsi que l’indice du premier octet. La méthode de secours doit renvoyer
truesi la mémoire tampon de secours peut fournir le ou les caractères constituant le meilleur choix de remplacement ; sinon, elle doit renvoyerfalse. Pour un repli en cas d'exception, la méthode de repli doit lever une exception.Méthode EncoderFallbackBuffer.GetNextChar ou DecoderFallbackBuffer.GetNextChar, appelée à plusieurs reprises par l’encodeur ou le décodeur pour obtenir le caractère suivant à partir 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.
EncoderFallbackBuffer.Remaining Ou DecoderFallbackBuffer.Remaining propriété, qui retourne le nombre de caractères restants dans la mémoire tampon de secours.
La méthode EncoderFallbackBuffer.MovePrevious ou DecoderFallbackBuffer.MovePrevious, qui déplace la position actuelle dans la mémoire tampon de secours vers le caractère précédent.
La méthode EncoderFallbackBuffer.Reset ou DecoderFallbackBuffer.Reset, qui réinitialise la mémoire tampon de secours.
Si l’implémentation de secours est une solution de secours appropriée ou une secours de remplacement, les classes dérivées EncoderFallbackBuffer et 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.
Un Exemple EncoderFallback
Un exemple précédent a utilisé le repli de remplacement pour remplacer les caractères Unicode qui ne correspondent pas aux caractères ASCII par un astérisque (*). L’exemple suivant utilise une implémentation de secours adaptée à la place pour fournir un meilleur mappage de caractères non ASCII.
Le code suivant définit une classe nommée CustomMapper dérivée de EncoderFallback laquelle gérer le mappage le mieux adapté des caractères non ASCII. Sa CreateFallbackBuffer méthode retourne un CustomMapperFallbackBuffer objet, qui fournit l’implémentation EncoderFallbackBuffer . La CustomMapper classe utilise un Dictionary<TKey,TValue> objet pour stocker les mappages de caractères Unicode non pris en charge (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). Pour rendre ce mappage disponible pour la mémoire tampon de secours, l’instance CustomMapper est passée en tant que paramètre au CustomMapperFallbackBuffer constructeur de classe. Étant donné que le mappage le plus long est la chaîne « INF » pour le caractère Unicode U+221E, la MaxCharCount propriété retourne 3.
public class CustomMapper : EncoderFallback
{
public string DefaultString;
internal Dictionary<ushort, ulong> mapping;
public CustomMapper() : this("*")
{
}
public CustomMapper(string defaultString)
{
this.DefaultString = defaultString;
// Create table of mappings
mapping = new Dictionary<ushort, ulong>();
mapping.Add(0x24C8, 0x53);
mapping.Add(0x2075, 0x35);
mapping.Add(0x221E, 0x49004E0046);
}
public override EncoderFallbackBuffer CreateFallbackBuffer()
{
return new CustomMapperFallbackBuffer(this);
}
public override int MaxCharCount
{
get { return 3; }
}
}
Public Class CustomMapper : Inherits EncoderFallback
Public DefaultString As String
Friend mapping As Dictionary(Of UShort, ULong)
Public Sub New()
Me.New("?")
End Sub
Public Sub New(ByVal defaultString As String)
Me.DefaultString = defaultString
' Create table of mappings
mapping = New Dictionary(Of UShort, ULong)
mapping.Add(&H24C8, &H53)
mapping.Add(&H2075, &H35)
mapping.Add(&H221E, &H49004E0046)
End Sub
Public Overrides Function CreateFallbackBuffer() As System.Text.EncoderFallbackBuffer
Return New CustomMapperFallbackBuffer(Me)
End Function
Public Overrides ReadOnly Property MaxCharCount As Integer
Get
Return 3
End Get
End Property
End Class
Le code suivant définit la CustomMapperFallbackBuffer classe, qui est dérivée de EncoderFallbackBuffer. Le dictionnaire qui contient les mappages les mieux adaptés et qui est défini dans l’instance CustomMapper est disponible à partir de son constructeur de classe. Sa Fallback méthode retourne true si l’un des caractères Unicode que l’encodeur ASCII ne peut pas encoder sont définis dans le dictionnaire de mappage ; sinon, il retourne false. Pour chaque solution de repli, la variable privée count indique le nombre de caractères qui restent à retourner, et la variable privée index indique la position, dans la mémoire tampon de chaîne charsToReturn, du caractère suivant à retourner.
public class CustomMapperFallbackBuffer : EncoderFallbackBuffer
{
int count = -1; // Number of characters to return
int index = -1; // Index of character to return
CustomMapper fb;
string charsToReturn;
public CustomMapperFallbackBuffer(CustomMapper fallback)
{
this.fb = fallback;
}
public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
{
// Do not try to map surrogates to ASCII.
return false;
}
public override bool Fallback(char charUnknown, int index)
{
// Return false if there are already characters to map.
if (count >= 1) return false;
// Determine number of characters to return.
charsToReturn = String.Empty;
ushort key = Convert.ToUInt16(charUnknown);
if (fb.mapping.ContainsKey(key)) {
byte[] bytes = BitConverter.GetBytes(fb.mapping[key]);
int ctr = 0;
foreach (var byt in bytes) {
if (byt > 0) {
ctr++;
charsToReturn += (char) byt;
}
}
count = ctr;
}
else {
// Return default.
charsToReturn = fb.DefaultString;
count = 1;
}
this.index = charsToReturn.Length - 1;
return true;
}
public override char GetNextChar()
{
// We'll return a character if possible, so subtract from the count of chars to return.
count--;
// If count is less than zero, we've returned all characters.
if (count < 0)
return '\u0000';
this.index--;
return charsToReturn[this.index + 1];
}
public override bool MovePrevious()
{
// Original: if count >= -1 and pos >= 0
if (count >= -1) {
count++;
return true;
}
else {
return false;
}
}
public override int Remaining
{
get { return count < 0 ? 0 : count; }
}
public override void Reset()
{
count = -1;
index = -1;
}
}
Public Class CustomMapperFallbackBuffer : Inherits EncoderFallbackBuffer
Dim count As Integer = -1 ' Number of characters to return
Dim index As Integer = -1 ' Index of character to return
Dim fb As CustomMapper
Dim charsToReturn As String
Public Sub New(ByVal fallback As CustomMapper)
MyBase.New()
Me.fb = fallback
End Sub
Public Overloads Overrides Function Fallback(ByVal charUnknownHigh As Char, ByVal charUnknownLow As Char, ByVal index As Integer) As Boolean
' Do not try to map surrogates to ASCII.
Return False
End Function
Public Overloads Overrides Function Fallback(ByVal charUnknown As Char, ByVal index As Integer) As Boolean
' Return false if there are already characters to map.
If count >= 1 Then Return False
' Determine number of characters to return.
charsToReturn = String.Empty
Dim key As UShort = Convert.ToUInt16(charUnknown)
If fb.mapping.ContainsKey(key) Then
Dim bytes() As Byte = BitConverter.GetBytes(fb.mapping.Item(key))
Dim ctr As Integer
For Each byt In bytes
If byt > 0 Then
ctr += 1
charsToReturn += Chr(byt)
End If
Next
count = ctr
Else
' Return default.
charsToReturn = fb.DefaultString
count = 1
End If
Me.index = charsToReturn.Length - 1
Return True
End Function
Public Overrides Function GetNextChar() As Char
' We'll return a character if possible, so subtract from the count of chars to return.
count -= 1
' If count is less than zero, we've returned all characters.
If count < 0 Then Return ChrW(0)
Me.index -= 1
Return charsToReturn(Me.index + 1)
End Function
Public Overrides Function MovePrevious() As Boolean
' Original: if count >= -1 and pos >= 0
If count >= -1 Then
count += 1
Return True
Else
Return False
End If
End Function
Public Overrides ReadOnly Property Remaining As Integer
Get
Return If(count < 0, 0, count)
End Get
End Property
Public Overrides Sub Reset()
count = -1
index = -1
End Sub
End Class
Le code suivant instancie ensuite l’objet CustomMapper et transmet une instance de celle-ci à la Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) méthode. La sortie indique que l’implémentation de secours la plus adaptée gère correctement les trois caractères non ASCII dans la chaîne d’origine.
using System;
using System.Collections.Generic;
using System.Text;
class Program
{
static void Main()
{
Encoding enc = Encoding.GetEncoding("us-ascii", new CustomMapper(), new DecoderExceptionFallback());
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
for (int ctr = 0; ctr <= str1.Length - 1; ctr++) {
Console.Write("{0} ", Convert.ToUInt16(str1[ctr]).ToString("X4"));
if (ctr == str1.Length - 1)
Console.WriteLine();
}
Console.WriteLine();
// Encode the original string using the ASCII encoder.
byte[] bytes = enc.GetBytes(str1);
Console.Write("Encoded bytes: ");
foreach (var byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine("\n");
// Decode the ASCII bytes.
string str2 = enc.GetString(bytes);
Console.WriteLine($"Round-trip: {str1.Equals(str2)}");
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
Imports System.Text
Imports System.Collections.Generic
Module Module1
Sub Main()
Dim enc As Encoding = Encoding.GetEncoding("us-ascii", New CustomMapper(), New DecoderExceptionFallback())
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&H24C8), ChrW(&H2075), ChrW(&H221E))
Console.WriteLine(str1)
For ctr As Integer = 0 To str1.Length - 1
Console.Write("{0} ", Convert.ToUInt16(str1(ctr)).ToString("X4"))
If ctr = str1.Length - 1 Then Console.WriteLine()
Next
Console.WriteLine()
' Encode the original string using the ASCII encoder.
Dim bytes() As Byte = enc.GetBytes(str1)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.WriteLine()
Console.WriteLine()
' Decode the ASCII bytes.
Dim str2 As String = enc.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module