Encodage de caractères dans .NET
Cet article fournit une introduction aux systèmes d’encodage de caractères utilisés par .NET. L’article explique comment les types String, Char, Rune et StringInfo fonctionnent avec Unicode, UTF-16 et UTF-8.
Le terme caractère est utilisé ici dans le sens général de ce qu’un lecteur considère comme un élément d’affichage unique. Les exemples courants sont la lettre « a », le symbole « @ » et l’emoji « 🐂 ». Parfois, ce qui ressemble à un caractère est en fait composé de plusieurs éléments d’affichage indépendants, comme l’explique la section sur les groupes de graphèmes.
Les types string et char
Une instance de la classe string représente du texte. Un string
est logiquement une séquence de valeurs 16 bits, chacune d’elles étant une instance du struct char. La propriété string.Length retourne le nombre d’instances char
dans l’instance string
.
L’exemple de fonction suivante imprime les valeurs en notation hexadécimale de toutes les instances char
d’un string
:
void PrintChars(string s)
{
Console.WriteLine($"\"{s}\".Length = {s.Length}");
for (int i = 0; i < s.Length; i++)
{
Console.WriteLine($"s[{i}] = '{s[i]}' ('\\u{(int)s[i]:x4}')");
}
Console.WriteLine();
}
Passez la string « Hello » à cette fonction et vous obtenez la sortie suivante :
PrintChars("Hello");
"Hello".Length = 5
s[0] = 'H' ('\u0048')
s[1] = 'e' ('\u0065')
s[2] = 'l' ('\u006c')
s[3] = 'l' ('\u006c')
s[4] = 'o' ('\u006f')
Chaque caractère est représenté par une valeur char
unique. Ce modèle est vrai pour la plupart des langues du monde. Par exemple, voici la sortie de deux caractères chinois qui ressemblent à nǐ hǎo et signifient Bonjour :
PrintChars("你好");
"你好".Length = 2
s[0] = '你' ('\u4f60')
s[1] = '好' ('\u597d')
Toutefois, pour certaines langues et pour certains symboles et emoji, il faut deux instances char
pour représenter un seul caractère. Par exemple, comparez les caractères et les instances char
dans le mot qui signifie Osage dans la langue Osage :
PrintChars("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟");
"𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟".Length = 17
s[0] = '�' ('\ud801')
s[1] = '�' ('\udccf')
s[2] = '�' ('\ud801')
s[3] = '�' ('\udcd8')
s[4] = '�' ('\ud801')
s[5] = '�' ('\udcfb')
s[6] = '�' ('\ud801')
s[7] = '�' ('\udcd8')
s[8] = '�' ('\ud801')
s[9] = '�' ('\udcfb')
s[10] = '�' ('\ud801')
s[11] = '�' ('\udcdf')
s[12] = ' ' ('\u0020')
s[13] = '�' ('\ud801')
s[14] = '�' ('\udcbb')
s[15] = '�' ('\ud801')
s[16] = '�' ('\udcdf')
Dans l’exemple précédent, chaque caractère à l’exception de l’espace est représenté par deux instances char
.
Un emoji Unicode unique est également représenté par deux char
s, comme illustré dans l’exemple suivant montrant un emoji ox :
"🐂".Length = 2
s[0] = '�' ('\ud83d')
s[1] = '�' ('\udc02')
Ces exemples montrent que la valeur de string.Length
, qui indique le nombre d’instances char
, n’indique pas nécessairement le nombre de caractères affichés. Une seule instance char
elle-même ne représente pas nécessairement un caractère.
Les paires char
qui sont mappées à un seul caractère sont appelées paires de substitution. Pour comprendre leur fonctionnement, vous devez comprendre l’encodage Unicode et UTF-16.
Points de code Unicode
Unicode est une norme d’encodage internationale à utiliser sur différentes plateformes et avec différents langages et scripts.
La norme Unicode définit plus de 1,1 million de points de code. Un point de code est une valeur entière pouvant aller de 0 à U+10FFFF
(1 114 111 décimale). Certains points de code sont attribués à des lettres, des symboles ou des emojis. D’autres sont affectés à des actions qui contrôlent la façon dont le texte ou les caractères sont affichés, par exemple passer à une nouvelle ligne. De nombreux points de code ne sont pas encore attribués.
Voici quelques exemples d’affectations de points de code, avec des liens vers les tableaux Unicode dans lesquels ils apparaissent :
Décimal | Hex | Exemple | Description |
---|---|---|---|
10 | U+000A |
N/A | SAUT DE LIGNE |
97 | U+0061 |
a | LETTRE MINUSCULE LATINE A |
562 | U+0232 |
Ȳ | LETTRE MAJUSCULE LATINE Y AVEC MACRON |
68 675 | U+10C43 |
𐱃 | LETTRE TURQUE ANCIENNE ORKHON AT |
127 801 | U+1F339 |
🌹 | Emoji ROSE |
Les points de code sont généralement référencés à l’aide de la syntaxe U+xxxx
, où xxxx
est la valeur entière encodée en hexadécimal.
Dans la plage complète de points de code, il existe deux sous-plages :
- Le plan multilingue de base (BMP) dans la plage
U+0000..U+FFFF
. Cette plage de 16 bits fournit 65 536 points de code, suffisamment pour couvrir la majorité des systèmes d’écriture du monde. - Points de code supplémentaires dans la plage
U+10000..U+10FFFF
. Cette plage de 21 bits fournit plus d’un million de points de code supplémentaires qui peuvent être utilisés pour des langues moins connues et d’autres fins telles que les emojis.
Le diagramme suivant illustre la relation entre le BMP et les points de code supplémentaires.
Unités de code UTF-16
Le format de transformation Unicode 16 bits (UTF-16) est un système d’encodage de caractères qui utilise des unités de code 16 bits pour représenter des points de code Unicode. .NET utilise UTF-16 pour encoder le texte dans une string
. Une instance char
représente une unité de code 16 bits.
Une seule unité de code 16 bits peut représenter n’importe quel point de code dans la plage 16 bits du plan multilingue de base. Toutefois, pour un point de code dans la plage supplémentaire, deux instances char
sont nécessaires.
Paires de substitution
La traduction de deux valeurs 16 bits vers une valeur 21 bits unique est facilitée par une plage spéciale appelée les points de code de substitution, de U+D800
à U+DFFF
(décimale 55 296 à 57 343), inclusivement.
Le diagramme suivant illustre la relation entre le BMP et les points de code de substitution.
Lorsqu’un point de code de substitution élevé (U+D800..U+DBFF
) est immédiatement suivi d’un point de code de substitution faible (U+DC00..U+DFFF
), la paire est interprétée comme un point de code supplémentaire à l’aide de la formule suivante :
code point = 0x10000 +
((high surrogate code point - 0xD800) * 0x0400) +
(low surrogate code point - 0xDC00)
Voici la même formule à l’aide de la notation décimale :
code point = 65,536 +
((high surrogate code point - 55,296) * 1,024) +
(low surrogate code point - 56,320)
Un point de code de substitution élevé n’a pas de valeur de nombre supérieure à un point de code de substitution faible. Le point de code de substitution élevé est appelé « élevé », car il est utilisé pour calculer les 10 bits d’ordre supérieur d’une plage de points de code 20 bits. Le point de code de substitution faible est utilisé pour calculer les 10 bits d’ordre inférieur.
Par exemple, le point de code réel qui correspond à la paire de substitution 0xD83C
et 0xDF39
est calculé comme suit :
actual = 0x10000 + ((0xD83C - 0xD800) * 0x0400) + (0xDF39 - 0xDC00)
= 0x10000 + ( 0x003C * 0x0400) + 0x0339
= 0x10000 + 0xF000 + 0x0339
= 0x1F339
Voici le même calcul à l’aide de la notation décimale :
actual = 65,536 + ((55,356 - 55,296) * 1,024) + (57,145 - 56320)
= 65,536 + ( 60 * 1,024) + 825
= 65,536 + 61,440 + 825
= 127,801
L’exemple précédent montre que "\ud83c\udf39"
est l’encodage UTF-16 du point de code U+1F339 ROSE ('🌹')
mentionné précédemment.
Valeurs scalaires Unicode
Le terme valeur scalaire Unicode fait référence à tous les points de code autres que les points de code de substitution. En d’autres termes, une valeur scalaire est n’importe quel point de code affecté à un caractère ou qui peut être affecté à un caractère à l’avenir. « Caractère » ici fait référence à tout ce qui peut être affecté à un point de code, ce qui inclut des actions qui contrôlent l’affichage du texte ou des caractères.
Le diagramme suivant illustre les points de code de valeur scalaire.
Type Rune en tant que valeur scalaire
Important
Le type Rune
n’est pas disponible dans .NET Framework.
Dans .NET, le type System.Text.Rune représente une valeur scalaire Unicode.
Les constructeurs Rune
valident que l’instance résultante est une valeur scalaire Unicode valide, sinon une exception est générée. L’exemple suivant montre le code qui instancie correctement les instances Rune
, car l’entrée représente des valeurs scalaires valides :
Rune a = new Rune('a');
Rune b = new Rune(0x0061);
Rune c = new Rune('\u0061');
Rune d = new Rune(0x10421);
Rune e = new Rune('\ud801', '\udc21');
L’exemple suivant lève une exception, car le point de code se trouve dans la plage de substitution et ne fait pas partie d’une paire de substitution :
Rune f = new Rune('\ud801');
L’exemple suivant lève une exception, car le point de code dépasse la plage supplémentaire :
Rune g = new Rune(0x12345678);
Exemple d’utilisation Rune : modification de la casse de lettre
Une API qui prend un char
et suppose qu’il fonctionne avec un code de caractère qui est une valeur scalaire ne fonctionne pas correctement si le char
provient d’une paire de substitution. Par exemple, considérez la méthode suivante qui appelle Char.ToUpperInvariant dans chaque char dans une string :
// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string ConvertToUpperBadExample(string input)
{
StringBuilder builder = new StringBuilder(input.Length);
for (int i = 0; i < input.Length; i++) /* or 'foreach' */
{
builder.Append(char.ToUpperInvariant(input[i]));
}
return builder.ToString();
}
Si la stringinput
contient la lettre déséret minuscule er
(𐑉
), ce code ne le convertit pas en majuscules (𐐡
). Le code appelle char.ToUpperInvariant
séparément sur chaque code de caractère de substitution, U+D801
et U+DC49
. Mais U+D801
n’a pas suffisamment d’informations par lui-même pour l’identifier comme une lettre minuscule, donc char.ToUpperInvariant
le laisse seul. Et il gère U+DC49
de la même façon. Le résultat est que la lettre minuscule « 𐑉 » dans la stringinput
n’est pas convertie en « 𐐡 » majuscule.
Voici deux options pour convertir correctement une string en majuscules :
Appelez String.ToUpperInvariant dans l’entrée string plutôt que d’effectuer une itération
char
-by-char
. La méthodestring.ToUpperInvariant
a accès aux deux parties de chaque paire de substitution. Elle peut donc gérer correctement tous les codes de caractère Unicode.Effectuez une itération dans les valeurs scalaires Unicode en tant qu’instances
Rune
au lieu d’instanceschar
, comme illustré dans l’exemple suivant. Étant donné qu’une instanceRune
est une valeur scalaire Unicode valide, elle peut être transmise aux API qui s’attendent à fonctionner sur une valeur scalaire. Par exemple, l’appel de Rune.ToUpperInvariant comme indiqué dans l’exemple suivant donne des résultats corrects :static string ConvertToUpper(string input) { StringBuilder builder = new StringBuilder(input.Length); foreach (Rune rune in input.EnumerateRunes()) { builder.Append(Rune.ToUpperInvariant(rune)); } return builder.ToString(); }
Autres API Rune
Le type Rune
expose des analogues de nombre des API char
. Par exemple, les méthodes suivantes reflètent les API statiques sur le type char
:
Pour obtenir la valeur scalaire brute d’une instance Rune
, utilisez la propriété Rune.Value.
Pour convertir une instance Rune
en séquence de char
s, utilisez Rune.ToString ou la méthode Rune.EncodeToUtf16.
Étant donné que toute valeur scalaire Unicode est représentée par un char
unique ou par une paire de substitution, n’importe quelle instance Rune
peut être représentée par au maximum 2 instances char
. Utilisez Rune.Utf16SequenceLength pour voir le nombre d’instances char
requises pour représenter une instance Rune
.
Pour plus d’informations sur le type .NET Rune
, consultez la référence de l’API Rune
.
Groupes de graphèmes
Ce qui ressemble à un caractère peut résulter d’une combinaison de plusieurs codes de caractère, de sorte qu’un terme plus descriptif qui est souvent utilisé à la place de « caractère » est le groupe de graphèmes. Le terme équivalent dans .NET est un élément de texte.
Considérez les instances string
« a », « á », « á » et « 👩🏽🚒
». Si votre système d’exploitation les gère comme spécifié par la norme Unicode, chacune de ces instances string
apparaît sous la forme d’un seul élément de texte ou d’un groupe de graphèmes. Toutefois, les deux derniers sont représentés par plusieurs codes de caractère de valeur scalaire.
La string « a » est représentée par une valeur scalaire et contient une instance
char
.U+0061 LATIN SMALL LETTER A
La string « á » est représentée par une valeur scalaire et contient une instance
char
.U+00E1 LATIN SMALL LETTER A WITH ACUTE
La string « á » ressemble à « á », mais est représentée par deux valeurs scalaires et contient deux instances
char
.U+0061 LATIN SMALL LETTER A
U+0301 COMBINING ACUTE ACCENT
Enfin, la string «
👩🏽🚒
» est représentée par quatre valeurs scalaires et contient sept instanceschar
.U+1F469 WOMAN
(plage supplémentaire, nécessite une paire de substitution)U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4
(plage supplémentaire, nécessite une paire de substitution)U+200D ZERO WIDTH JOINER
U+1F692 FIRE ENGINE
(plage supplémentaire, nécessite une paire de substitution)
Dans certains des exemples précédents, tels que le modificateur d’accentuation combiné ou le modificateur de ton de peau, le code de caractère ne s’affiche pas en tant qu’élément autonome sur l’écran. Au lieu de cela, il sert à modifier l’apparence d’un élément de texte qui est venu avant lui. Ces exemples montrent qu’il peut prendre plusieurs valeurs scalaires pour constituer ce que nous considérons comme un seul « caractère » ou « groupe de graphèmes ».
Pour énumérer les groupes de graphèmes d’une string
, utilisez la classe StringInfo comme indiqué dans l’exemple suivant. Si vous connaissez Swift, le type .NET StringInfo
est conceptuellement similaire au type character
de Swift.
Exemple : nombre char, Rune et instances d’élément de texte
Dans les API .NET, un groupe de graphèmes est appelé un élément de texte. La méthode suivante illustre les différences entre char
, Rune
et les instances d’élément de texte dans une string
:
static void PrintTextElementCount(string s)
{
Console.WriteLine(s);
Console.WriteLine($"Number of chars: {s.Length}");
Console.WriteLine($"Number of runes: {s.EnumerateRunes().Count()}");
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(s);
int textElementCount = 0;
while (enumerator.MoveNext())
{
textElementCount++;
}
Console.WriteLine($"Number of text elements: {textElementCount}");
}
PrintTextElementCount("a");
// Number of chars: 1
// Number of runes: 1
// Number of text elements: 1
PrintTextElementCount("á");
// Number of chars: 2
// Number of runes: 2
// Number of text elements: 1
PrintTextElementCount("👩🏽🚒");
// Number of chars: 7
// Number of runes: 4
// Number of text elements: 1
Exemple : fractionnement d’instances string
Lors du fractionnement d’instances string
, évitez de fractionner les paires de substitution et les groupes de graphèmes. Prenons l’exemple suivant de code incorrect, qui envisage d’insérer des sauts de ligne tous les 10 caractères dans une string :
// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string InsertNewlinesEveryTencharsBadExample(string input)
{
StringBuilder builder = new StringBuilder();
// First, append chunks in multiples of 10 chars
// followed by a newline.
int i = 0;
for (; i < input.Length - 10; i += 10)
{
builder.Append(input, i, 10);
builder.AppendLine(); // newline
}
// Then append any leftover data followed by
// a final newline.
builder.Append(input, i, input.Length - i);
builder.AppendLine(); // newline
return builder.ToString();
}
Étant donné que ce code énumère les instances char
, une paire de substitution qui se produit pour chevaucher une limite de 10 char
sera fractionnée et une nouvelle ligne injectée entre elles. Cette insertion introduit une altération des données, car les codes de caractère de substitution sont significatifs uniquement en tant que paires.
Le risque d’altération des données n’est pas éliminé si vous énumérez des instances Rune
(valeurs scalaires) au lieu d’instances char
. Un ensemble d’instances Rune
peut constituer un groupe de graphèmes qui chevauche une limite de 10 char
. Si le jeu de groupes de graphèmes est fractionné, il ne peut pas être interprété correctement.
Une meilleure approche consiste à décomposer la string en comptant les groupes de graphèmes, ou éléments de texte, comme dans l’exemple suivant :
static string InsertNewlinesEveryTenTextElements(string input)
{
StringBuilder builder = new StringBuilder();
// Append chunks in multiples of 10 chars
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(input);
int textElementCount = 1;
while (enumerator.MoveNext())
{
builder.Append(enumerator.Current);
if (textElementCount % 10 == 0 && textElementCount > 0)
{
builder.AppendLine(); // newline
}
textElementCount++;
}
// Add a final newline.
builder.AppendLine(); // newline
return builder.ToString();
}
Comme indiqué précédemment, avant .NET 5, la classe StringInfo
avait un bogue entraînant la gestion incorrecte de certains groupes de graphèmes.
UTF-8 et UTF-32
Les sections précédentes se sont concentrées sur UTF-16, car c’est ce que .NET utilise pour encoder des instances string
. Il existe d’autres systèmes d’encodage pour Unicode : UTF-8 et UTF-32. Ces encodages utilisent respectivement des unités de code 8 bits et des unités de code 32 bits.
Comme UTF-16, UTF-8 nécessite plusieurs unités de code pour représenter certaines valeurs scalaires Unicode. UTF-32 peut représenter n’importe quelle valeur scalaire dans une seule unité de code 32 bits.
Voici quelques exemples montrant comment le même code de caractère Unicode est représenté dans chacun de ces trois systèmes d’encodage Unicode :
Scalar: U+0061 LATIN SMALL LETTER A ('a')
UTF-8 : [ 61 ] (1x 8-bit code unit = 8 bits total)
UTF-16: [ 0061 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 00000061 ] (1x 32-bit code unit = 32 bits total)
Scalar: U+0429 CYRILLIC CAPITAL LETTER SHCHA ('Щ')
UTF-8 : [ D0 A9 ] (2x 8-bit code units = 16 bits total)
UTF-16: [ 0429 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 00000429 ] (1x 32-bit code unit = 32 bits total)
Scalar: U+A992 JAVANESE LETTER GA ('ꦒ')
UTF-8 : [ EA A6 92 ] (3x 8-bit code units = 24 bits total)
UTF-16: [ A992 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 0000A992 ] (1x 32-bit code unit = 32 bits total)
Scalar: U+104CC OSAGE CAPITAL LETTER TSHA ('𐓌')
UTF-8 : [ F0 90 93 8C ] (4x 8-bit code units = 32 bits total)
UTF-16: [ D801 DCCC ] (2x 16-bit code units = 32 bits total)
UTF-32: [ 000104CC ] (1x 32-bit code unit = 32 bits total)
Comme indiqué précédemment, une seule unité de code UTF-16 d’une paire de substitution n’a aucun sens. De la même manière, une seule unité de code UTF-8 n’a pas de sens en soi si elle fait partie d’une séquence de deux, trois ou quatre unités utilisées pour calculer une valeur scalaire.
Notes
À compter de C# 11, vous pouvez représenter des littéraux string UTF-8 à l’aide du suffixe « u8 » sur un littéral string. Pour plus d’informations sur les littéraux UTF-8 string, consultez la section « littéraux string » de l’article sur les types de référence intégrés dans le Guide C#.
Endianness
Dans .NET, les unités de code UTF-16 d’une string sont stockées dans la mémoire contiguë sous la forme d’une séquence d’entiers 16 bits (instances char
). Les bits des unités de code individuelles sont disposés en fonction du mode Endian de l’architecture actuelle.
Sur une architecture en mode Little Endian, la string consistant en codes de caractère UTF-16 [ D801 DCCC ]
serait disposée dans la mémoire en tant qu’octets [ 0x01, 0xD8, 0xCC, 0xDC ]
. Sur une architecture en mode Big Endian, cette même string serait disposée dans la mémoire en tant qu’octets [ 0xD8, 0x01, 0xDC, 0xCC ]
.
Les systèmes informatiques qui communiquent entre eux doivent s’entendre sur la représentation des données qui traversent le câble. La plupart des protocoles réseau utilisent UTF-8 comme norme lors de la transmission de texte, en partie pour éviter les problèmes pouvant résulter d’une machine en mode Big Endian communiquant avec une machine en mode Little Endian. La string consistant en codes de caractère UTF-8 [ F0 90 93 8C ]
sera toujours représentée en tant qu’octets [ 0xF0, 0x90, 0x93, 0x8C ]
indépendamment du mode Endian.
Pour utiliser UTF-8 pour transmettre du texte, les applications .NET utilisent souvent du code comme l’exemple suivant :
string stringToWrite = GetString();
byte[] stringAsUtf8Bytes = Encoding.UTF8.GetBytes(stringToWrite);
await outputStream.WriteAsync(stringAsUtf8Bytes, 0, stringAsUtf8Bytes.Length);
Dans l’exemple précédent, la méthode Encoding.UTF8.GetBytes décode la string
UTF-16 dans une série de valeurs scalaires Unicode, puis ré-encode ces valeurs scalaires en UTF-8 et place la séquence obtenue dans un tableau byte
. La méthode Encoding.UTF8.GetString effectue la transformation opposée, en convertissant un tableau UTF-8 byte
en UTF-16 string
.
Avertissement
Comme UTF-8 est courant sur Internet, il peut être tentant de lire des octets bruts à partir du câble et de traiter les données comme s’il s’agissait d’UTF-8. Toutefois, vous devez vérifier qu’il est en effet bien formé. Un client malveillant peut envoyer un UTF-8 mal formé à votre service. Si vous utilisez ces données comme si elles étaient bien formées, cela pourrait entraîner des erreurs ou des failles de sécurité dans votre application. Pour valider les données UTF-8, vous pouvez utiliser une méthode telle que Encoding.UTF8.GetString
, qui effectue la validation lors de la conversion des données entrantes en un string
.
Encodage bien formé
Un encodage Unicode bien formé est une string d’unités de code qui peut être décodée sans ambiguïté et sans erreur dans une séquence de valeurs scalaires Unicode. Les données bien formées peuvent être transcodées librement entre UTF-8, UTF-16 et UTF-32.
La question de savoir si une séquence d’encodage est bien formée ou non n’est pas liée au mode Endian de l’architecture d’une machine. Une séquence UTF-8 mal formée est mal formée de la même façon sur les machines Big Endian et Little Endian.
Voici quelques exemples d’encodages mal formés :
Dans UTF-8, la séquence
[ 6C C2 61 ]
est mal formée, carC2
ne peut pas être suivi de61
.Dans UTF-16, la séquence
[ DC00 DD00 ]
(ou, en C#, la string"\udc00\udd00"
) est mal formée, car le substitut faibleDC00
ne peut pas être suivi d’un autre substitut faibleDD00
.Dans UTF-32, la séquence
[ 0011ABCD ]
est mal formée, car0011ABCD
se trouve en dehors de la plage de valeurs scalaires Unicode.
Dans .NET, les instances string
contiennent presque toujours des données UTF-16 bien formées, mais cela n’est pas garanti. Les exemples suivants montrent du code C# valide qui crée des données UTF-16 mal formées dans des instances string
.
Littéral mal formé :
const string s = "\ud800";
Une sous-chaîne qui fractionne une paire de substitution :
string x = "\ud83e\udd70"; // "🥰" string y = x.Substring(1, 1); // "\udd70" standalone low surrogate
Les API telles que Encoding.UTF8.GetString
ne retournent jamais des instances string
mal formées. Les méthodes Encoding.GetString
et Encoding.GetBytes
détectent les séquences mal formées dans l’entrée et effectuent une substitution de caractères lors de la génération de la sortie. Par exemple, si Encoding.ASCII.GetString(byte[])
voit un octet non ASCII dans l’entrée (en dehors de la plage U+0000.U+007F), il insère un « ? » dans l’instance string
retournée. Encoding.UTF8.GetString(byte[])
remplace les séquences UTF-8 mal formées par U+FFFD REPLACEMENT CHARACTER ('�')
dans l’instance string
retournée. Pour plus d’informations, consultez la norme Unicode, sections 5.22 et 3.9.
Les classes intégrées Encoding
peuvent également être configurées pour lever une exception plutôt que pour effectuer une substitution de caractères lorsque des séquences mal formées sont vues. Cette approche est souvent utilisée dans les applications sensibles à la sécurité où la substitution de caractères peut ne pas être acceptable.
byte[] utf8Bytes = ReadFromNetwork();
UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
string asString = encoding.GetString(utf8Bytes); // will throw if 'utf8Bytes' is ill-formed
Pour plus d’informations sur l’utilisation des classes Encoding
intégrées, consultez Comment utiliser l’encodage de caractères dans .NET.