Partager via


Encodage de caractères dans .NET

Cet article fournit une introduction aux systèmes d’encodage character 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 character 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 character 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 character 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 characters chinois qui ressemblent à nǐ hǎo et signifient Hello :

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 character. Par exemple, comparez les characters 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 character à l’exception de l’espace est représenté par deux instances char.

Un emoji Unicode unique est également représenté par deux chars, 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 characters affichés. Une seule instance char elle-même ne représente pas nécessairement un character.

Les paires char qui sont mappées à un seul character 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 characters 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 charts Unicode dans lesquels ils apparaissent :

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

BMP et 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 character 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.

BMP et 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 character ou qui peut être affecté à un character à 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 characters.

Le diagramme suivant illustre les points de code de valeur scalaire.

Valeurs scalaires

Type Rune en tant que valeur scalaire

À compter de .NET Core 3.0, le type System.Text.Rune représente une valeur scalaire Unicode. Rune n’est pas disponible dans .NET Core 2.x ou .NET Framework 4.x.

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éthode string.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’instances char, comme illustré dans l’exemple suivant. Étant donné qu’une instance Rune 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 chars, 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 character 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 « character » 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 instances char.

    • 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 « character » 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

Si vous exécutez ce code dans .NET Framework ou .NET Core 3.1 ou version antérieure, le nombre d’éléments de texte pour l’emoji affiche 4. Cela est dû à un bogue dans la classe StringInfo qui a été corrigé dans .NET 5.

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 characters 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, car C2 ne peut pas être suivi de 61.

  • Dans UTF-16, la séquence [ DC00 DD00 ] (ou, en C#, la string"\udc00\udd00") est mal formée, car le substitut faible DC00 ne peut pas être suivi d’un autre substitut faible DD00.

  • Dans UTF-32, la séquence [ 0011ABCD ] est mal formée, car 0011ABCD 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";
    
  • Substring 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 character 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 character 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 character 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 character dans .NET.

Voir aussi