Teckenkodning i .NET

Den här artikeln innehåller en introduktion till character-kodningssystem som används av .NET. Artikeln förklarar hur typerna String, Char, Runeoch StringInfo fungerar med Unicode, UTF-16 och UTF-8.

Termen charakter används här i allmän mening av vad en läsare uppfattar som ett enda visningselement. Vanliga exempel är bokstaven "a", symbolen "@" och emojin "🐂". Ibland består det som ser ut som om en charakter faktiskt består av flera oberoende visningselement, som avsnittet i grapheme-kluster förklarar.

Typerna string och char

En instans av string klassen representerar viss text. A string är logiskt en sekvens med 16-bitars värden, som var och en är en instans av structen char . . string Egenskapen Length returnerar antalet char instanser i instansen string .

Följande exempelfunktion skriver ut värdena i hexadecimal notation för alla char instanser i en 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();
}

string Skicka "Hello" till den här funktionen så får du följande utdata:

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

Varje charakter representeras av ett enda char värde. Det mönstret gäller för de flesta av världens språk. Här är till exempel utdata för två kinesiska charakterare som låter som nǐ hǎo och medelvärdet Hello:

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

Men för vissa språk och för vissa symboler och emojis krävs det två char instanser för att representera en enda charakter. Jämför till exempel charakters och char instanser i ordet som betyder Osage på osagespråket:

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

I föregående exempel representeras varje charakter utom utrymmet av två char instanser.

En enda Unicode-emoji representeras också av två chars, vilket visas i följande exempel som visar en ox-emoji:

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

De här exemplen visar att värdet string.Lengthför , som anger antalet char instanser, inte nödvändigtvis anger antalet visade charakter. En enskild char instans i sig representerar inte nödvändigtvis en charakter.

De char par som mappar till en enskild charakter kallas surrogatpar. För att förstå hur de fungerar måste du förstå Unicode- och UTF-16-kodning.

Unicode-kodpunkter

Unicode är en internationell kodningsstandard för användning på olika plattformar och med olika språk och skript.

Unicode Standard definierar över 1,1 miljoner kodpunkter. En kodpunkt är ett heltalsvärde som kan variera från 0 till U+10FFFF (decimalt 1 114 111). Vissa kodpunkter tilldelas bokstäver, symboler eller emojier. Andra tilldelas till åtgärder som styr hur text eller charakters visas, till exempel gå vidare till en ny rad. Många kodpunkter har ännu inte tilldelats.

Här följer några exempel på kodpunktstilldelningar med länkar till Unicode-ts chardär de visas:

Decimal Hex Exempel beskrivning
10 U+000A Ej tillämpligt RADMATNING
97 U+0061 f LATINSK LITEN BOKSTAV A
562 U+0232 Ȳ LATINSK VERSAL BOKSTAV Y MED MACRON
68,675 U+10C43 𐱃 GAMMAL TURKIC MÄRKA ORKHON PÅ
127,801 U+1F339 🌹 ROSE-emoji

Kodpunkter refereras vanligtvis till med hjälp av syntaxen U+xxxx, där xxxx är det hexkodade heltalsvärdet.

Inom det fullständiga intervallet med kodpunkter finns det två underområden:

  • Det grundläggande flerspråkiga planet (BMP) i intervallet U+0000..U+FFFF. Det här 16-bitarsintervallet ger 65 536 kodpunkter, tillräckligt för att täcka de flesta av världens skrivsystem.
  • Tilläggskodpunkter i intervallet U+10000..U+10FFFF. Det här 21-bitarsintervallet ger mer än en miljon ytterligare kodpunkter som kan användas för mindre välkända språk och andra syften, till exempel emojis.

Följande diagram illustrerar relationen mellan BMP och tilläggskodpunkterna.

BMP och tilläggskodpunkter

UTF-16-kodenheter

16-bitars Unicode Transformation Format (UTF-16) är ett charakterkodningssystem som använder 16-bitars kodenheter för att representera Unicode-kodpunkter. .NET använder UTF-16 för att koda texten i en string. En char instans representerar en 16-bitars kodenhet.

En enda 16-bitars kodenhet kan representera valfri kodpunkt i 16-bitarsintervallet för det grundläggande flerspråkiga planet. Men för en kodpunkt i tilläggsintervallet behövs två char instanser.

Surrogatpar

Översättningen av två 16-bitarsvärden till ett enda 21-bitarsvärde underlättas av ett särskilt intervall som kallas surrogatkodpunkter, från U+D800 till U+DFFF (decimalt 55 296 till 57 343), inklusive.

Följande diagram illustrerar relationen mellan BMP och surrogatkodpunkterna.

BMP- och surrogatkodpunkter

När en hög surrogatkodpunkt (U+D800..U+DBFF) omedelbart följs av en låg surrogatkodpunkt (U+DC00..U+DFFF), tolkas paret som en kompletterande kodpunkt med hjälp av följande formel:

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

Här är samma formel med decimal notation:

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

En hög surrogatkodpunkt har inte ett högre talvärde än en låg surrogatkodpunkt. Den höga surrogatkodpunkten kallas "hög" eftersom den används för att beräkna 10 bitar i högre ordning i ett 20-bitars kodpunktsintervall. Den låga surrogatkodpunkten används för att beräkna 10 bitar i lägre ordning.

Till exempel den faktiska kodpunkten som motsvarar surrogatparet 0xD83C och 0xDF39 beräknas på följande sätt:

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

Här är samma beräkning med decimal notation:

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

Föregående exempel visar att "\ud83c\udf39" är UTF-16-kodningen för kodpunkten U+1F339 ROSE ('🌹') som nämndes tidigare.

Unicode-skalärvärden

Termen Unicode-skalärvärde refererar till alla andra kodpunkter än surrogatkodpunkterna. Med andra ord är ett skalärt värde en kodpunkt som tilldelas en charakter eller som kan tilldelas en charakter i framtiden. "Tecken" här refererar till allt som kan tilldelas till en kodpunkt, som innehåller sådant som åtgärder som styr hur text eller charakters visas.

Följande diagram illustrerar de skalära värdekodpunkterna.

Skalära värden

Typen Rune som ett skalärt värde

Från och med .NET Core 3.0 System.Text.Rune representerar typen ett Unicode-skalärvärde. Rune är inte tillgängligt i .NET Core 2.x eller .NET Framework 4.x.

Konstruktorerna Rune verifierar att den resulterande instansen är ett giltigt Unicode-skalärt värde, annars utlöser de ett undantag. I följande exempel visas kod som omedelbart instanser Rune eftersom indata representerar giltiga skalärvärden:

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

I följande exempel genereras ett undantag eftersom kodpunkten ligger i surrogatintervallet och inte ingår i ett surrogatpar:

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

I följande exempel genereras ett undantag eftersom kodpunkten ligger utanför tilläggsintervallet:

Rune g = new Rune(0x12345678);

Rune användningsexempel: ändra bokstavsfall

Ett API som tar ett char och förutsätter att det fungerar med en kodpunkt som är ett skalärt värde fungerar inte korrekt om char är från ett surrogatpar. Tänk till exempel på följande metod som anropar Char.ToUpperInvariant var och char en i en 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();
}

Om innehåller inputstring gemener deseret bokstaven er (𐑉), konverterar den här koden inte den till versaler (𐐡). Koden anropar char.ToUpperInvariant separat på varje surrogatkodpunkt U+D801 och U+DC49. Men U+D801 har inte tillräckligt med information i sig för att identifiera den som en gemen bokstav, så char.ToUpperInvariant lämnar den ensam. Och det hanterar U+DC49 på samma sätt. Resultatet är att gemener inputstring 𐑉i inte konverteras till versaler 𐑉.

Här är två alternativ för korrekt konvertering av en string till versaler:

  • Anropa String.ToUpperInvariant indata string i stället för att iterera char-by-char. Metoden string.ToUpperInvariant har åtkomst till båda delarna av varje surrogatpar, så att den kan hantera alla Unicode-kodpunkter korrekt.

  • Iterera genom Unicode-skalärvärdena som Rune instanser i stället för char instanser, som du ser i följande exempel. Eftersom en Rune instans är ett giltigt Unicode-skalärt värde kan den skickas till API:er som förväntar sig att fungera med ett skalärt värde. Till exempel ger anrop Rune.ToUpperInvariant som visas i följande exempel rätt resultat:

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

Andra Rune API:er

Typen Rune exponerar analoger för många av API:erna char . Följande metoder speglar till exempel statiska API:er på char typen:

Om du vill hämta det råa skalärvärdet från en Rune instans använder du Rune.Value egenskapen .

Om du vill konvertera en Rune instans tillbaka till en sekvens med chars använder Rune.ToString du eller Rune.EncodeToUtf16 metoden.

Eftersom alla Unicode-skalärvärden kan representeras av ett enskilt char par eller ett surrogatpar, kan alla Rune instanser representeras av högst 2 char instanser. Använd Rune.Utf16SequenceLength för att se hur många char instanser som krävs för att representera en Rune instans.

Mer information om .NET-typen Rune finns i API-referensenRune.

Grapheme-kluster

Det som ser ut som en charakter kan bero på en kombination av flera kodpunkter, så en mer beskrivande term som ofta används i stället för "charakter" är grapheme-kluster. Motsvarande term i .NET är textelement.

string Tänk på instanserna "a", "á", "á" och "👩🏽‍🚒". Om operativsystemet hanterar dem enligt Unicode-standarden visas var och en av dessa string instanser som ett enda textelement eller grapheme-kluster. Men de två sista representeras av mer än en skalbar värdekodpunkt.

  • string "A" representeras av ett skalärt värde och innehåller en char instans.

    • U+0061 LATIN SMALL LETTER A
  • string "á" representeras av ett skalärt värde och innehåller en char instans.

    • U+00E1 LATIN SMALL LETTER A WITH ACUTE
  • string "á" ser likadan ut som "á" men representeras av två skalärvärden och innehåller två char instanser.

    • U+0061 LATIN SMALL LETTER A
    • U+0301 COMBINING ACUTE ACCENT
  • Slutligen string representeras "👩🏽‍🚒" av fyra skalära värden och innehåller sju char instanser.

    • U+1F469 WOMAN (tilläggsintervall, kräver ett surrogatpar)
    • U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (tilläggsintervall, kräver ett surrogatpar)
    • U+200D ZERO WIDTH JOINER
    • U+1F692 FIRE ENGINE (tilläggsintervall, kräver ett surrogatpar)

I några av de föregående exemplen , till exempel kombinationsmodifieraren eller hudtonsmodifieraren, visas inte kodpunkten som ett fristående element på skärmen. I stället används det för att ändra utseendet på ett textelement som kom före det. De här exemplen visar att det kan krävas flera skalära värden för att utgöra det vi ser som en enda "charakter" eller "grapheme-kluster".

Om du vill räkna upp grapheme-kluster i en stringanvänder du StringInfo klassen enligt följande exempel. Om du är bekant med Swift är .NET-typen StringInfo konceptuellt lik Swifts character typ.

Exempel: antal char, Runeoch textelementinstanser

I .NET-API:er kallas ett grapheme-kluster för ett textelement. Följande metod visar skillnaderna mellan char, Runeoch textelementinstanser i en 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

Om du kör den här koden i .NET Framework eller .NET Core 3.1 eller tidigare visas 4antalet textelement för emojin . Det beror på en bugg i StringInfo klassen som har åtgärdats i .NET 5.

Exempel: dela instanser string

När du delar instanser string bör du undvika att dela surrogatpar och grapheme-kluster. Tänk på följande exempel på felaktig kod som har för avsikt att infoga radbrytningar var tionde charakters i en 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();
}

Eftersom den här koden räknar upp char instanser delas ett surrogatpar som råkar korsa en 10-gränschar och en ny rad matas in mellan dem. Den här infogningen introducerar skadade data eftersom surrogatkodpunkter endast är meningsfulla som par.

Risken för skadade data elimineras inte om du räknar Rune upp instanser (skalärvärden) i stället för char instanser. En uppsättning Rune instanser kan utgöra ett grapheme-kluster som sträcker sig över en 10-gränschar . Om grapheme-klusteruppsättningen delas upp kan den inte tolkas korrekt.

En bättre metod är att bryta string genom att räkna grapheme-kluster eller textelement, som i följande exempel:

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

}

Som tidigare nämnts, före .NET 5, hade klassen en bugg som StringInfo gjorde att vissa grapheme-kluster hanterades felaktigt.

UTF-8 och UTF-32

Föregående avsnitt fokuserade på UTF-16 eftersom det är vad .NET använder för att koda string instanser. Det finns andra kodningssystem för Unicode – UTF-8 och UTF-32. Dessa kodningar använder 8-bitars kodenheter respektive 32-bitars kodenheter.

Precis som UTF-16 kräver UTF-8 flera kodenheter för att representera vissa Unicode-skalärvärden. UTF-32 kan representera valfritt skalärt värde i en enda 32-bitars kodenhet.

Här är några exempel som visar hur samma Unicode-kodpunkt representeras i vart och ett av dessa tre Unicode-kodningssystem:

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)

Som tidigare nämnts är en enda UTF-16-kodenhet från ett surrogatpar meningslös av sig själv. På samma sätt är en enda UTF-8-kodenhet meningslös i sig själv om den är i en sekvens med två, tre eller fyra som används för att beräkna ett skalärt värde.

Kommentar

Från och med C# 11 kan du representera UTF-8-literaler string med suffixet "u8" på en literal string. Mer information om UTF-8-literaler string finns i avsnittet "string literaler" i artikeln om inbyggda referenstyper i C#-guiden.

Endianness

I .NET lagras UTF-16-kodenheterna i ett string sammanhängande minne som en sekvens med 16-bitars heltal (char instanser). Bitarna av enskilda kodenheter anges enligt den aktuella arkitekturens slutpunkt .

I en lite endiansk arkitektur string skulle består av UTF-16-kodpunkterna [ D801 DCCC ] anges i minnet som byte .[ 0x01, 0xD8, 0xCC, 0xDC ] I en stor endiansk arkitektur skulle samma string sak läggas ut i minnet som byteen [ 0xD8, 0x01, 0xDC, 0xCC ].

Datorsystem som kommunicerar med varandra måste komma överens om representationen av data som korsar tråden. De flesta nätverksprotokoll använder UTF-8 som standard när text överförs, delvis för att undvika problem som kan uppstå när en stor endiansk dator kommunicerar med en liten endiansk dator. Den string bestående av UTF-8-kodpunkterna [ F0 90 93 8C ] representeras alltid som byte [ 0xF0, 0x90, 0x93, 0x8C ] oavsett endianness.

Om du vill använda UTF-8 för att överföra text använder .NET-program ofta kod som i följande exempel:

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

I föregående exempel avkodar metoden Encoding.UTF8.GetBytes UTF-16 string tillbaka till en serie Unicode-skalärvärden och kodar sedan om dessa skalärvärden till UTF-8 och placerar den resulterande sekvensen i en byte matris. Metoden Encoding.UTF8.GetString utför den motsatta omvandlingen och konverterar en UTF-8-matris byte till en UTF-16 string.

Varning

Eftersom UTF-8 är vanligt på Internet kan det vara frestande att läsa råa byte från kabeln och att behandla data som om de vore UTF-8. Du bör dock kontrollera att den verkligen är välformulerad. En skadlig klient kan skicka oformaterad UTF-8 till din tjänst. Om du använder dessa data som om de vore välformulerad kan det orsaka fel eller säkerhetshål i ditt program. Om du vill verifiera UTF-8-data kan du använda en metod som Encoding.UTF8.GetString, som utför validering när inkommande data konverteras till en string.

Välformulerad kodning

En välformulerad Unicode-kodning är en string av kodenheter som kan avkodas entydigt och utan fel i en sekvens med Unicode-skalärvärden. Välformulerade data kan omkodas fritt fram och tillbaka mellan UTF-8, UTF-16 och UTF-32.

Frågan om en kodningssekvens är välformulerad eller inte har inget samband med endianiteten i en dators arkitektur. En illa utformad UTF-8-sekvens är dåligt utformad på samma sätt på både stora och små endianska maskiner.

Här följer några exempel på illa utformade kodningar:

  • I UTF-8 är sekvensen [ 6C C2 61 ] dåligt utformad eftersom C2 den inte kan följas av 61.

  • I UTF-16 är sekvensen [ DC00 DD00 ] (eller, i C#, string"\udc00\udd00") dåligt utformad eftersom den låga surrogaten DC00 inte kan följas av en annan låg surrogat DD00.

  • I UTF-32 är sekvensen [ 0011ABCD ] dåligt utformad eftersom 0011ABCD den ligger utanför intervallet för Unicode-skalärvärden.

I .NET string innehåller instanser nästan alltid välformulerade UTF-16-data, men det är inte garanterat. I följande exempel visas giltig C#-kod som skapar oformaterad UTF-16-data i string instanser.

  • En illa utformad literal:

    const string s = "\ud800";
    
  • En undergruppstring som delar upp ett surrogatpar:

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

API:er som Encoding.UTF8.GetString aldrig returnerar oformaterade string instanser. Encoding.GetString och Encoding.GetBytes metoder identifierar illa formade sekvenser i indata och utför charaktererersättning när utdata genereras. Om Encoding.ASCII.GetString(byte[]) du till exempel ser en icke-ASCII-byte i indata (utanför intervallet U+0000..U+007F) infogar den en "?" i den returnerade string instansen. Encoding.UTF8.GetString(byte[]) ersätter felaktigt formade UTF-8-sekvenser med U+FFFD REPLACEMENT CHARACTER ('�') i den returnerade string instansen. Mer information finns i Unicode Standard, avsnitten 5.22 och 3.9.

De inbyggda klasserna kan också konfigureras för att utlösa ett undantag i Encoding stället för att utföra charaktererersättning när oformaterade sekvenser visas. Den här metoden används ofta i säkerhetskänsliga program där charaktererersättning kanske inte är acceptabelt.

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

Information om hur du använder de inbyggda klasserna finns i Encoding Använda character-kodningsklasser i .NET.

Se även