Kódování znaků v rozhraní .NET

Tento článek obsahuje úvod do systémů kódování znaků, které používají .NET. Tento článek vysvětluje, jak typy String, Char, Rune a StringInfo fungují s Unicode, UTF-16 a UTF-8.

Znak se zde používá v obecném smyslu toho, co čtenář vnímá jako jediný zobrazovací prvek. Mezi běžné příklady patří písmeno "a", symbol "@" a emoji "🐂". Někdy se zdá, že jeden znak se ve skutečnosti skládá z několika nezávislých prvků zobrazení, jak vysvětluje část o grafémových klastrech.

Typy string a char

Instance string třídy představuje nějaký text. A string je logicky posloupnost 16bitových hodnot, z nichž každá je instancí char struktury. string. Vlastnost Length vrátí počet char instancí v string instanci.

Následující ukázková funkce vypíše hodnoty v šestnáctkovém zápisu všech instancí char v 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();
}

Předejte "Hello" této funkci a zobrazí se následující výstup:

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

Každý znak je reprezentován jednou char hodnotou. Tento vzor platí pro většinu světových jazyků. Tady je například výstup dvou čínských znaků, které zní jako nǐ hǎo a znamenají Hello:

PrintChars("你好");
"你好".Length = 2
s[0] = '你' ('\u4f60')
s[1] = '好' ('\u597d')

Ale pro některé jazyky a některé symboly a emoji jsou potřeba dvě char instance, které představují jeden znak. Porovnejte například znaky a char instance ve slově, které v jazyce Osage znamená 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')

V předchozím příkladu je každý znak s výjimkou mezery reprezentován dvěma char instancemi.

Jeden znak Unicode emoji je také reprezentován dvěma chars, jak je vidět v následujícím příkladu znázorňujícím emoji vola:

"🐂".Length = 2
s[0] = '�' ('\ud83d')
s[1] = '�' ('\udc02')

Tyto příklady ukazují, že hodnota string.Length, která označuje počet char instancí, nemusí nutně znamenat počet zobrazených znaků. Jedna char instance sama o sobě nemusí nutně představovat znak.

Dvojice char , které odpovídají jednomu znaku, se nazývají náhradní páry. Abyste pochopili, jak fungují, musíte porozumět kódování Unicode a UTF-16.

Kódy Unicode

Unicode je mezinárodní kódovací standard pro použití na různých platformách a s různými jazyky a skripty.

Standard Unicode definuje více než 1,1 milionu bodů kódu. Bod kódu je celočíselná hodnota, která může být v rozsahu od 0 do U+10FFFF (desítková hodnota 1 114 111). Některé body kódu se přiřazují písmenům, symbolům nebo emoji. Ostatním se přiřadí akce, které určují, jak se zobrazí text nebo znaky, například přechod na nový řádek. Mnoho bodů kódu ještě není přiřazeno.

Tady je několik příkladů přiřazení bodů kódu s odkazy na grafy Unicode, ve kterých se zobrazují:

Desetinné číslo Hexadecimální Příklad Popis
10 U+000A Nenalezeno POSUN O ŘÁDEK
97 U+0061 a MALÉ PÍSMENO LATINKY A
562 U+0232 Ȳ VELKÉ PÍSMENO LATINKY Y S MAKRONEM
68,675 U+10C43 𐱃 STARÉ TURKICKÉ PÍSMENO ORKHON AT
127,801 U+1F339 🌹 RŮŽE emoji

Body kódu se obvykle označují pomocí syntaxe U+xxxx, kde xxxx je celočíselná hodnota zakódovaná šestnáctkovým kódem.

V celém rozsahu bodů kódu existují dvě poduspořádky:

  • Základní Vícejazyčná Rovina (BMP) v rozsahu U+0000..U+FFFF. Tento 16bitový rozsah poskytuje 65 536 bodů kódu, který stačí k pokrytí většiny systémů psaní na světě.
  • Doplňkové body kódu v rozsahu U+10000..U+10FFFF. Tento 21bitový rozsah poskytuje více než milion dalších bodů kódu, které lze použít pro méně známé jazyky a další účely, jako jsou emoji.

Následující diagram znázorňuje vztah mezi BMP a doplňkovými body kódu.

BMP a doplňkové kódové body

Jednotky kódu UTF-16

16bitový formát transformace Unicode (UTF-16) je systém kódování znaků, který používá 16bitové jednotky kódu k reprezentaci bodů kódu Unicode. .NET používá UTF-16 ke kódování textu v souboru string. Instance char představuje 16bitovou jednotku kódu.

Jedna 16bitová jednotka kódu může představovat libovolný bod kódu v 16bitovém rozsahu základní vícejazyčné roviny. Pro bod kódu v doplňkovém rozsahu jsou však potřeba dvě char instance.

Náhradní páry

Překlad dvou 16bitových hodnot na jednu 21bitovou hodnotu usnadňuje zvláštní rozsah označovaný jako náhradní body kódu od U+D800 do U+DFFF (desítkové číslo 55 296 až 57 343), včetně.

Následující diagram znázorňuje vztah mezi BMP a náhradními body kódu.

BMP a náhradní kódové body

Pokud je náhradní vysoký kódový bod () okamžitě následován náhradním nízkým kódovým bodem (), pár se interpretuje jako doplňkový kódový bod pomocí následujícího vzorce:

code point = 0x10000 +
  ((high surrogate code point - 0xD800) * 0x0400) +
  (low surrogate code point - 0xDC00)

Tady je stejný vzorec, který používá desetinnou notaci:

code point = 65,536 +
  ((high surrogate code point - 55,296) * 1,024) +
  (low surrogate code point - 56,320)

Vysoký náhradní kódový bod nemá vyšší číselnou hodnotu než nízký náhradní kódový bod. Vysoký zástupný kódový bod se nazývá vysoký, protože se používá k výpočtu vyšších 10 bitů v rámci 20bitového rozsahu kódového bodu. K výpočtu 10 bitů nižšího řádu se používá nízký náhradní bod kódu.

Například skutečný bod kódu, který odpovídá náhradnímu páru 0xD83C , a 0xDF39 vypočítá se následujícím způsobem:

actual = 0x10000 + ((0xD83C - 0xD800) * 0x0400) + (0xDF39 - 0xDC00)
       = 0x10000 + (          0x003C  * 0x0400) +           0x0339
       = 0x10000 +                      0xF000  +           0x0339
       = 0x1F339

Tady je stejný výpočet pomocí desetinného zápisu:

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

Předchozí příklad ukazuje, že "\ud83c\udf39" je UTF-16 kódováním kódového bodu U+1F339 ROSE ('🌹'), zmíněného dříve.

Skalární hodnoty Unicode

Termín Skalární hodnota Unicode odkazuje na všechny jiné body kódu než náhradní body kódu. Jinými slovy, skalární hodnota je jakýkoli kódový bod, kterému je přiřazen znak nebo kterému může být znak přiřazen v budoucnu. Znak zde odkazuje na cokoli, co je možné přiřadit k bodu kódu, což zahrnuje například akce, které řídí způsob zobrazení textu nebo znaků.

Následující diagram znázorňuje skalární body kódu hodnoty.

Skalární hodnoty

Typ Rune jako skalární hodnota

Důležité

Tento Rune typ není k dispozici v rozhraní .NET Framework.

V .NET typ System.Text.Rune představuje skalární hodnotu Unicode.

Konstruktory Rune ověřují, že výsledná instance je platná skalární hodnota Unicode, jinak vyvolá výjimku. Následující příklad ukazuje kód, který úspěšně vytváří instance Rune, protože vstup představuje platné skalární hodnoty:

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');

Následující příklad vyvolá výjimku, protože bod kódu je v náhradní oblasti a není součástí náhradní dvojice:

Rune f = new Rune('\ud801');

Následující příklad vyvolá výjimku, protože bod kódu je nad rámec doplňkového rozsahu:

Rune g = new Rune(0x12345678);

Rune Příklad použití: změna velikosti písmen

Rozhraní API, které přijímá char a předpokládá, že pracuje s kódovým bodem, který je skalární hodnotou, nebude fungovat správně, pokud char pochází z náhradního páru. Představte si například následující metodu, která volá Char.ToUpperInvariant na každém char v 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();
}

input string Pokud obsahuje malá písmena Deseret er (𐑉), tento kód ho nepřevedne na velká písmena (𐐡). Kód volá char.ToUpperInvariant samostatně na každém náhradním kódovém bodu U+D801 a U+DC49. Ale U+D801 nemá dostatek informací, aby je identifikoval jako malá písmena, takže char.ToUpperInvariant je ponechá sám. A zvládne U+DC49 to stejným způsobem. Výsledkem je, že se malá písmena 𐑉v inputstring souboru nepřevedou na velká písmena 𐑉.

Tady jsou dvě možnosti správného převodu string na velká písmena:

  • Zavolejte String.ToUpperInvariant na vstupu string místo iterativního zpracování char-po-char. Metoda string.ToUpperInvariant má přístup k oběma částem každého náhradního páru, takže dokáže správně zpracovat všechny body kódu Unicode.

  • Iterujte přes skalární hodnoty Unicode jako Rune instance místo char instancí, jak je znázorněno v následujícím příkladu. Jelikož je Rune instance platná skalární hodnota Unicode, lze ji předat rozhraním API, která očekávají, že budou fungovat na skalární hodnotě. Například volání Rune.ToUpperInvariant , jak je znázorněno v následujícím příkladu, poskytuje správné výsledky:

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

Jiná Rune rozhraní API

Typ Rune zveřejňuje analogy mnoha char rozhraní API. Například následující metody zrcadlí statická rozhraní API typu char :

K získání nezpracované skalární hodnoty z Rune instance použijte Rune.Value vlastnost.

Chcete-li převést Rune instanci zpět na posloupnost chars, použijte Rune.ToString nebo metodu Rune.EncodeToUtf16 .

Vzhledem k tomu, že skalární hodnota Unicode je reprezentována jedním char nebo náhradním párem, může každá Rune instance reprezentovat maximálně 2 char instance. Použijte Rune.Utf16SequenceLength a zjistěte, kolik instancí char je potřeba k zobrazení instance Rune.

Další informace o typu .NET najdete v referenčních informacích rozhraní API.

Graphemové clustery

Vypadá to, že jeden znak může být výsledkem kombinace více kódových bodů, takže popisnější termín, který se často používá místo „znaku“, je grafémový klastr. Ekvivalentní termín v .NET je textový prvek.

Zohledněte string instance "a", "á", "á" a "👩🏽‍🚒". Pokud je operační systém zpracovává podle standardu Unicode, zobrazí se každá z těchto string instancí jako jeden textový prvek nebo cluster grapheme. Poslední dva jsou ale reprezentovány více než jedním skalárním bodem kódu hodnoty.

  • " string a" je reprezentován jednou skalární hodnotou a obsahuje jednu char instanci.

    • U+0061 LATIN SMALL LETTER A
  • Hodnota string "á" je reprezentována jednou skalární hodnotou a obsahuje jednu char instanci.

    • U+00E1 LATIN SMALL LETTER A WITH ACUTE
  • " string á" vypadá stejně jako "á", ale je reprezentován dvěma skalárními hodnotami a obsahuje dvě char instance.

    • U+0061 LATIN SMALL LETTER A
    • U+0301 COMBINING ACUTE ACCENT
  • Nakonec je "string👩🏽‍🚒" reprezentován čtyřmi skalárními hodnotami a obsahuje sedm char instancí.

    • U+1F469 WOMAN (doplňkový rozsah, vyžaduje náhradní pár)
    • U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (doplňkový rozsah, vyžaduje náhradní pár)
    • U+200D ZERO WIDTH JOINER
    • U+1F692 FIRE ENGINE (doplňkový rozsah, vyžaduje náhradní pár)

V některých z předchozích příkladů – například kombinace modifikátoru zvýraznění nebo modifikátor tónu kůže – se bod kódu nezobrazuje jako samostatný prvek na obrazovce. Slouží spíše ke změně vzhledu textového prvku, který byl před ním. Tyto příklady ukazují, že může trvat několik skalárních hodnot, aby se z nich vymyslel jediný znak nebo cluster grapheme.

K zobrazení výčtu clusterů grafeme v objektu string, použijte StringInfo třídu, jak je znázorněno v následujícím příkladu. Pokud znáte Swift, typ .NET StringInfo je koncepčně podobný character Swiftu.

Příklad: počet char, Rune, a instance textových elementů

V rozhraních .NET API se cluster grapheme nazývá textový prvek. Následující metoda ukazuje rozdíly mezi char, Runea text element instance v 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

Příklad: Rozdělení string instancí

Při rozdělování string instancí se vyhněte rozdělování surrogátních párů a grafémových clusterů. Podívejte se na následující příklad nesprávného kódu, který má v úmyslu stringvložit konce řádků každých 10 znaků v :

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

Vzhledem k tomu, že tento kód vypíše char instance, náhradní dvojice, která náhodně překročí hranici 10-char, bude rozdělena a mezi nimi bude vložen nový řádek. Toto vložení představuje poškození dat, protože náhradní body kódu jsou smysluplné pouze jako páry.

Potenciál poškození dat není eliminován, pokud vypíšete Rune instance místo char instancí (skalární hodnoty). Sada instancí Rune může vytvořit grafémový cluster, který se váže na hranici 10 char. Pokud je sada clusteru grapheme rozdělená, není možné ji správně interpretovat.

Lepším přístupem je počítání shluků grafémů nebo textových prvků, jak je znázorněno v následujícím příkladu: string.

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)
        {
            builder.AppendLine(); // newline
        }
        textElementCount++;
    }

    // Add a final newline.
    builder.AppendLine(); // newline
    return builder.ToString();

}

Jak jsme uvedli dříve, před .NET 5 měla třída chybu, StringInfo která způsobovala nesprávné zpracování některých clusterů grapheme.

UTF-8 a UTF-32

Předchozí části se zaměřily na UTF-16, protože to je to, co .NET používá ke kódování string instancí. Existují další kódovací systémy pro Unicode - UTF-8 a UTF-32. Tato kódování používají 8bitové jednotky kódu a 32bitové jednotky kódu.

Podobně jako UTF-16 vyžaduje UTF-8 několik jednotek kódu, které představují některé skalární hodnoty Unicode. UTF-32 může představovat libovolnou skalární hodnotu v jedné 32bitové jednotce kódu.

Tady je několik příkladů, které ukazují, jak je stejný bod kódu Unicode reprezentován v každém z těchto tří systémů kódování 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)

Jak už bylo zmíněno dříve, jedna jednotka kódu UTF-16 z náhradní dvojice je bezvýznamná sama o sobě. Stejně tak je jedna jednotka kódu UTF-8 bezvýznamná sama o sobě, pokud je v posloupnosti dvou, tří nebo čtyř používaných k výpočtu skalární hodnoty.

Poznámka:

Počínaje jazykem C# 11 můžete reprezentovat literály UTF-8 string pomocí přípony "u8" v literálu string. Další informace o literálech UTF-8 string najdete v části "string literály" článku o integrovaných odkazových typech v příručce jazyka C#.

Endiánství

V .NET jsou jednotky string kódu UTF-16 uložené v souvislé paměti jako posloupnost 16bitových celých čísel (char instancí). Bity jednotlivých jednotek kódu jsou rozloženy podle endianness aktuální architektury.

V malé-endové architektuře by string skládající se z bodů kódu [ D801 DCCC ] UTF-16 byly rozloženy do paměti jako bajty [ 0x01, 0xD8, 0xCC, 0xDC ]. Na architektuře big-endian by stejná string byla rozložena v paměti jako bajty [ 0xD8, 0x01, 0xDC, 0xCC ].

Počítačové systémy, které spolu komunikují, musí souhlasit s reprezentací dat překračovaných drátem. Většina síťových protokolů používá UTF-8 jako standard při přenosu textu, což je zčásti kvůli tomu, aby se předešlo problémům, které mohou vzniknout při komunikaci big-endian stroje s little-endian strojem. Prvky , které obsahují kódové body UTF-8 , budou vždy reprezentovány jako bajty , bez ohledu na pořadí bajtů.

Pokud chcete pro přenos textu použít UTF-8, aplikace .NET často používají kód jako v následujícím příkladu:

string stringToWrite = GetString();
byte[] stringAsUtf8Bytes = Encoding.UTF8.GetBytes(stringToWrite);
await outputStream.WriteAsync(stringAsUtf8Bytes, 0, stringAsUtf8Bytes.Length);

V předchozím příkladu metoda Encoding.UTF8.GetBytes dekóduje UTF-16 string zpět do řady skalárních hodnot Unicode a pak tyto skalární hodnoty znovu zakóduje do UTF-8 a umístí výslednou sekvenci do byte pole. Metoda Encoding.UTF8.GetString provádí opačnou transformaci a převádí pole UTF-8 byte na UTF-16 string.

Upozornění

Vzhledem k tomu, že UTF-8 je běžné na internetu, může být lákavé číst nezpracované bajty z drátu a považovat data za UTF-8. Měli byste ale ověřit, že je skutečně dobře vytvořená. Škodlivý klient může do vaší služby odeslat špatně formátovaný UTF-8. Pokud s daty pracujete tak, jako by byla dobře vytvořená, mohlo by to způsobit chyby nebo bezpečnostní díry ve vaší aplikaci. K ověření dat UTF-8 můžete použít metodu, jako je Encoding.UTF8.GetString, která provede ověření při převodu příchozích dat na string.

Dobře formátované kódování

Kódování Unicode ve správném formátu je string jednotek kódu, které lze dekódovat jednoznačně a bez chyb do posloupnosti skalárních hodnot Unicode. Dobře formátovaná data lze volně překódovat mezi UTF-8, UTF-16 a UTF-32.

Otázka, jestli je sekvence kódování správně vytvořená nebo ne, nesouvisí s endianness architektury počítače. Špatně vytvořená sekvence UTF-8 je špatně vytvořená stejným způsobem na big-endian stejně jako na little-endian počítačích.

Tady je několik příkladů kódování ve špatném formátu:

  • V UTF-8 je sekvence [ 6C C2 61 ] chybná, protože C2 nemůže následovat za 61.

  • V UTF-16 je sekvence [ DC00 DD00 ] (nebo v jazyce C#) špatně vytvořená, string"\udc00\udd00"protože nízká náhrada DC00 nemůže být následovana jinou nízkou náhradou DD00.

  • V UTF-32 je sekvence [ 0011ABCD ] špatně vytvořená, protože 0011ABCD je mimo rozsah skalárních hodnot Unicode.

V .NET string instance téměř vždy obsahují dobře formátovaná data UTF-16, ale to není zaručeno. Následující příklady ukazují platný kód jazyka C#, který vytváří chybná UTF-16 data v instancích string.

  • Literál s chybným tvarem:

    const string s = "\ud800";
    
  • Podřetězec, který rozdělí náhradní dvojici:

    string x = "\ud83e\udd70"; // "🥰"
    string y = x.Substring(1, 1); // "\udd70" standalone low surrogate
    

Rozhraní API, jako Encoding.UTF8.GetString, nikdy nevrací nesprávně vytvořené string instance. Encoding.GetString a Encoding.GetBytes metody při generování výstupu detekují špatně vytvořené sekvence ve vstupu a provádějí nahrazování znaků. Pokud Encoding.ASCII.GetString(byte[]) ve vstupu například uvidí bajt, který není ASCII (mimo rozsah U+0000..U+007F), vloží do vrácené instance string "?". Encoding.UTF8.GetString(byte[]) nahradí špatně vytvořené sekvence U+FFFD REPLACEMENT CHARACTER ('�') UTF-8 ve vrácené string instanci. Další informace najdete v článcích Standard Unicode, Oddíly 5.22 a 3.9.

Předdefinované Encoding třídy lze také nakonfigurovat tak, aby vyvolaly výjimku místo nahrazení znaků, pokud jsou zobrazeny špatně vytvořené sekvence. Tento přístup se často používá v aplikacích citlivých na zabezpečení, kde nahrazení znaků nemusí být přijatelné.

byte[] utf8Bytes = ReadFromNetwork();
UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
string asString = encoding.GetString(utf8Bytes); // will throw if 'utf8Bytes' is ill-formed

Informace o tom, jak používat předdefinované třídy, naleznete v Encoding tématu Jak používat třídy kódování znaků v .NET.

Viz také