Megosztás a következőn keresztül:


Karakterkódolás a .NET-ben

Ez a cikk bemutatja a .NET által használt karakterkódoló rendszereket. A cikk bemutatja, hogyan működnek a UnicodeCharRune, az UTF-16 és StringInfo az StringUTF-8 típusok.

A karakter kifejezést itt annak általános értelmében használják, hogy az olvasó mit érzékel egyetlen megjelenítési elemként. Gyakori példák az "a" betű, a "@" szimbólum és az emoji "🐂". Néha az egy karakternek tűnő karakter valójában több független megjelenítési elemből áll, ahogy azt a grapheme-fürtök szakasza is ismerteti.

Az string és a char típusok

Az osztály egy példánya string egy szöveget jelöl. Az A string logikailag 16 bites értékek sorozata, amelyek mindegyike a char szerkezet egy-egy példánya. A string. A Length tulajdonság a példány példányainak char string számát adja vissza.

Az alábbi mintafüggvény a következő példában szereplő összes példány hexadecimális jelölésében char lévő stringértékeket nyomtatja ki:

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

Adja át a string "Hello" értéket ennek a függvénynek, és a következő kimenetet kapja:

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

Minden karaktert egyetlen char érték jelöl. Ez a minta a világ legtöbb nyelvére igaz. Itt látható például két olyan kínai karakter kimenete, amelyek nǐ hǎo és a hello kifejezéshez hasonlóak:

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

Egyes nyelveknél, valamint egyes szimbólumok és hangulatjelek esetében azonban két char példány szükséges egyetlen karakter megjelenítéséhez. Hasonlítsa össze például a szóban szereplő karaktereket és char példányokat, amelyek az Osage nyelvet jelentik:

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

Az előző példában a szóköz kivételével minden karaktert két char példány jelöl.

Egyetlen Unicode emojit két chars is jelöl, ahogy az alábbi példában látható egy ökör emoji:

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

Ezek a példák azt mutatják, hogy a példányok számát char jelző érték string.Lengthnem feltétlenül jelzi a megjelenített karakterek számát. Egyetlen char példány önmagában nem feltétlenül jelöl karaktert.

Azokat char a párokat, amelyek egyetlen karakterre vannak képezve, helyettesítő pároknak nevezzük. A működésük megértéséhez ismernie kell a Unicode és az UTF-16 kódolást.

Unicode-kódpontok

A Unicode egy nemzetközi kódolási szabvány, amely különböző platformokon és különböző nyelveken és szkriptekben használható.

A Unicode Standard több mint 1,1 millió kódpontot határoz meg. A kódpont egy egész szám, amely 0 U+10FFFF és (decimális 1 114 1111) között lehet. Egyes kódpontok betűkhez, szimbólumokhoz vagy emojikhoz vannak rendelve. Mások olyan műveletekhez vannak rendelve, amelyek szabályozzák a szöveg vagy a karakterek megjelenítését, például egy új sorra való ugrást. Sok kódpont még nincs hozzárendelve.

Íme néhány példa a kódpont-hozzárendelésekre, amelyekben Unicode-diagramokra mutató hivatkozások találhatók:

Decimális Rontás Példa Leírás
10 U+000A n/a VONALCSATORNA
97 U+0061 a LATIN KIS A BETŰ
562 U+0232 Ȳ LATIN NAGYBETŰ Y MACRONNAL
68,675 U+10C43 𐱃 RÉGI TÜRK BETŰ ORKHON AT
127,801 U+1F339 🌹 ROSE emoji

A kódpontokra szokás hivatkozni a szintaxis U+xxxxhasználatával, ahol xxxx a hexán kódolt egész szám értéke van.

A kódpontok teljes tartományán belül két altartomány található:

  • Az alapszintű többnyelvű sík (BMP) a tartományban U+0000..U+FFFF. Ez a 16 bites tartomány 65 536 kódpontot biztosít, ami elegendő ahhoz, hogy lefedje a világ írási rendszereinek többségét.
  • A tartomány U+10000..U+10FFFFkiegészítő kódpontjai. Ez a 21 bites tartomány több mint egymillió további kódpontot biztosít, amelyek kevésbé ismert nyelvekhez és más célokra, például emojikhoz használhatók.

Az alábbi ábra a BMP és a kiegészítő kódpontok közötti kapcsolatot mutatja be.

BMP és kiegészítő kódpontok

UTF-16 kódegységek

A 16 bites Unicode-átalakítási formátum (UTF-16) egy karakterkódoló rendszer, amely 16 bites kódegységeket használ a Unicode-kódpontok megjelenítéséhez. A .NET az UTF-16 használatával kódolja a szöveget egy string. A char példányok egy 16 bites kódegységet jelölnek.

Egyetlen 16 bites kódegység az alapszintű többnyelvű sík 16 bites tartományának bármely kódpontját képviselheti. A kiegészítő tartomány kódpontjaihoz azonban két char példányra van szükség.

Helyettesítő párok

A két 16 bites érték egyetlen 21 bites értékre való fordítását egy speciális tartomány, az úgynevezett helyettesítő kódpontok segítik elő, U+D800 U+DFFF amelyek között a (decimális 55 296 és 57 343 közötti) értékek szerepelnek.

Az alábbi ábra a BMP és a helyettesítő kódpontok közötti kapcsolatot mutatja be.

BMP és helyettesítő kódpontok

Ha egy magas helyettesítő kódpontot (U+D800..U+DBFF) azonnal alacsony helyettesítő kódpont (U+DC00..U+DFFF) követ, a pár kiegészítő kódpontként értelmezve az alábbi képlettel:

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

Ugyanez a képlet decimális jelöléssel:

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

A magas helyettesítő kódpontnak nincs nagyobb számértéke, mint egy alacsony helyettesítő kódpontnak. A magas helyettesítő kódpont neve "magas", mivel a 20 bites kódponttartomány magasabbrendű 10 bitjének kiszámítására szolgál. Az alacsony helyettesítő kódpont az alacsonyabb rendű 10 bit kiszámítására szolgál.

Például a helyettesítő párnak 0xD83C megfelelő tényleges kódpont, amely 0xDF39 a következőképpen van kiszámítva:

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

Ugyanez a számítás decimális jelöléssel:

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

Az előző példa azt mutatja be, hogy "\ud83c\udf39" a korábban említett kódpont UTF-16 kódolása U+1F339 ROSE ('🌹') .

Unicode skaláris értékek

A Unicode skaláris érték kifejezés a helyettesítő kódpontoktól eltérő összes kódpontra vonatkozik. Más szóval a skaláris érték bármely olyan kódpont, amely egy karakterhez van hozzárendelve, vagy a jövőben hozzárendelhető egy karakterhez. A "Karakter" itt mindenre utal, amely hozzárendelhető egy kódponthoz, beleértve például a szöveg vagy a karakterek megjelenítését vezérlő műveleteket.

Az alábbi ábra a skaláris érték kódpontjait szemlélteti.

Skaláris értékek

A Rune típus skaláris értékként

Fontos

A Rune típus nem érhető el .NET-keretrendszer.

A .NET-ben a System.Text.Rune típus Unicode skaláris értéket jelöl.

A Rune konstruktorok ellenőrzik, hogy az eredményül kapott példány érvényes Unicode skaláris érték-e, ellenkező esetben kivételt okoznak. Az alábbi példa olyan kódot mutat be, amely sikeresen példányosítja Rune a példányokat, mert a bemenet érvényes skaláris értékeket jelöl:

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

Az alábbi példa kivételt jelez, mert a kódpont a helyettesítő tartományban van, és nem része helyettesítő párnak:

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

Az alábbi példa kivételt jelez, mert a kódpont túllépi a kiegészítő tartományt:

Rune g = new Rune(0x12345678);

Rune használati példa: betűs kis- és nagybetűk módosítása

Egy olyan API, amely egy char skaláris értéket tartalmazó kódponttal dolgozik, nem működik megfelelően, ha az char helyettesítő párból származik. Vegyük például a következő metódust, amely Char.ToUpperInvariant meghívja az egyeseket char a következőben 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();
}

Ha a inputstring kisbetűs Deseret betűt er (𐑉) tartalmazza, ez a kód nem konvertálja nagybetűvé (𐐡nagybetűvé). A kód minden helyettesítő kódponton külön hív char.ToUpperInvariant , U+D801 és U+DC49. De U+D801 önmagában nem rendelkezik elegendő információval ahhoz, hogy kisbetűként azonosítsa, ezért char.ToUpperInvariant hagyja békén. És ugyanúgy kezeli U+DC49 . Az eredmény az, hogy a inputstring "𐑉" kisbetű nem lesz "𐑉" nagybetűssé konvertálva.

A nagybetűk helyes átalakításának string két lehetősége van:

  • A bemenet meghívása String.ToUpperInvariant az iterálás charhelyett - by-char.string A string.ToUpperInvariant metódus hozzáféréssel rendelkezik az egyes helyettesítő párok mindkét részéhez, így minden Unicode-kódpontot megfelelően kezelhet.

  • Az alábbi példában látható módon a Unicode skaláris értékeit példányok helyett char példányokként Rune kell iterálni. Mivel a Rune példány érvényes Unicode skaláris érték, átadható az API-knak, amelyek várhatóan skaláris értéken fognak működni. A következő példában látható hívás Rune.ToUpperInvariant például helyes eredményeket ad:

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

Egyéb Rune API-k

A Rune típus számos char API analógjait teszi elérhetővé. A következő metódusok például statikus API-kat tükröznek a char típuson:

A nyers skaláris érték példányból Rune való lekéréséhez használja a tulajdonságot Rune.Value .

Ha vissza szeretne konvertálni egy példányt Rune s sorozattá char, használja vagy használja Rune.ToString a metódust Rune.EncodeToUtf16 .

Mivel bármely Unicode skaláris érték egyetlen char vagy helyettesítő pár által ábrázolható, a Rune példányokat legfeljebb 2 char példány jelölheti. Itt Rune.Utf16SequenceLength tekintheti meg, hogy hány char példányra van szükség egy Rune példány megjelenítéséhez.

A .NET Rune típusával kapcsolatos további információkért tekintse meg az Rune API-hivatkozást.

Grapheme-fürtök

Egy karakter több kódpont kombinációjából eredhet, ezért a "karakter" helyett gyakran használt leíróbb kifejezés a grapheme-fürt. A .NET egyenértékű kifejezése szövegelem.

Vegye figyelembe az "a string ", "á", "á" és "👩🏽‍🚒" példányokat. Ha az operációs rendszer a Unicode szabvány által meghatározott módon kezeli őket, mindegyik string példány egyetlen szöveges elemként vagy gráffürtként jelenik meg. Az utolsó kettőt azonban egynél több skaláris értékkódpont jelöli.

  • Az string "a"-t egy skaláris érték jelöli, és egy példányt char tartalmaz.

    • U+0061 LATIN SMALL LETTER A
  • Az string "á"-t egy skaláris érték jelöli, és egy példányt char tartalmaz.

    • U+00E1 LATIN SMALL LETTER A WITH ACUTE
  • Az string "á" ugyanúgy néz ki, mint az "á", de két skaláris érték jelöli, és két char példányt tartalmaz.

    • U+0061 LATIN SMALL LETTER A
    • U+0301 COMBINING ACUTE ACCENT
  • Végül a string "👩🏽‍🚒" értékét négy skaláris érték jelöli, és hét char példányt tartalmaz.

    • U+1F469 WOMAN (kiegészítő tartomány, pótpárt igényel)
    • U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (kiegészítő tartomány, pótpárt igényel)
    • U+200D ZERO WIDTH JOINER
    • U+1F692 FIRE ENGINE (kiegészítő tartomány, pótpárt igényel)

Néhány fenti példában - például az ékezetmódosító vagy a bőr tónusmódosító - a kódpont nem jelenik meg önálló elemként a képernyőn. Ehelyett egy előtte lévő szövegelem megjelenését módosítja. Ezek a példák azt mutatják, hogy több skaláris értékre is szükség lehet ahhoz, hogy egyetlen "karakterként" vagy "grapheme-fürtként" definiáljuk azt, amit gondolunk.

Egy stringadott gráffürtök számbavételéhez használja az osztályt az StringInfo alábbi példában látható módon. Ha ismeri a Swiftet, a .NET StringInfo típus fogalmilag hasonló a Swift típusáhozcharacter.

Példa: darabszám char, Runeés szövegelem-példányok

A .NET API-kban a gráffürtöt szöveges elemnek nevezzük. Az alábbi módszer bemutatja a következőkben szereplő , Runeés szövegelem-példányok közötti charkülönbségeketstring:

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élda: példányok felosztása string

Példányok felosztásakor string kerülje a helyettesítő párok és a gráffürtök felosztását. Tekintse meg a következő példát a helytelen kódra, amely 10 karakterenként szeretne sortöréseket beszúrni a stringkövetkezőbe:

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

Mivel ez a kód számba char veszi a példányokat, egy helyettesítő pár, amely történetesen egy 10-határtchar keresztez, fel lesz osztva, és egy új vonal lesz beszúrva közöttük. Ez a beszúrás adatsérülést vezet be, mivel a helyettesítő kódpontok csak párként értelmezhetők.

Az adatsérülés lehetősége nem szűnik meg, ha példányok helyett char példányokat (skaláris értékeket) sorol Rune fel. A példányok halmaza Rune olyan gráffürtöt alkothat, amely egy 10-határtchar gördít össze. Ha a gráffürt-készlet fel van osztva, az nem értelmezhető megfelelően.

A jobb módszer a string gráffürtök vagy szöveges elemek számlálásával történő megtörése, ahogyan az alábbi példában is látható:

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

}

Ahogy korábban említettük, a .NET 5 előtt az StringInfo osztályban hiba történt, ami miatt néhány gráffürt helytelenül lett kezelve.

UTF-8 és UTF-32

Az előző szakaszok az UTF-16-ra összpontosítottak, mert a .NET ezt használja a példányok kódolásához string . Vannak más kódolási rendszerek a Unicode - UTF-8 és UTF-32. Ezek a kódolások 8 bites kódegységeket és 32 bites kódegységeket használnak.

Az UTF-16-hoz hasonlóan az UTF-8-hoz is több kódegységre van szükség bizonyos Unicode skaláris értékek megjelenítéséhez. Az UTF-32 bármilyen skaláris értéket képviselhet egyetlen 32 bites kódegységben.

Íme néhány példa arra, hogyan jelenik meg ugyanaz a Unicode-kódpont a három Unicode kódolási rendszer mindegyikében:

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)

Ahogy korábban említettük, egyetlen UTF-16 kódegység egy helyettesítő párból önmagában értelmetlen. Ugyanígy egyetlen UTF-8 kódegység önmagában is értelmetlen, ha egy skaláris érték kiszámításához két, három vagy négy sorozatban van.

Feljegyzés

A C# 11-től kezdve az UTF-8 string literálokat az "u8" utótaggal jelölheti egy literálon string. Az UTF-8 string literálokkal kapcsolatos további információkért tekintse meg a C# útmutató beépített referenciatípusait ismertető cikk "stringliterálok" szakaszát.

Endianness

A .NET-ben az UTF-16 kódegységek string egybefüggő memóriában vannak tárolva, 16 bites egész számok (char példányok) sorozataként. Az egyes kódegységek bitjei az aktuális architektúra endianitásának megfelelően vannak meghatározva.

Egy kis endian architektúrában az string UTF-16 kódpontokból álló kódpontokat [ D801 DCCC ] a memóriában bájtként [ 0x01, 0xD8, 0xCC, 0xDC ]helyeznék el. Egy olyan big endian architektúrán, amelyet a memória a bájtokkal [ 0xD8, 0x01, 0xDC, 0xCC ]megegyező string módon helyezne el.

Az egymással kommunikáló számítógépes rendszereknek meg kell egyeznie a vezetéken áthaladó adatok ábrázolásában. A legtöbb hálózati protokoll az UTF-8 szabványt használja a szövegek továbbításához, részben azért, hogy elkerülje azokat a problémákat, amelyek egy kis endian géppel kommunikáló big endian gépekből eredhetnek. Az string UTF-8 kódpontokból álló kódpontok [ F0 90 93 8C ] mindig bájtként [ 0xF0, 0x90, 0x93, 0x8C ] jelennek meg, függetlenül az endianitástól.

Ha az UTF-8-at szeretné használni a szöveg továbbításához, a .NET-alkalmazások gyakran az alábbi példához hasonló kódot használnak:

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

Az előző példában a Encoding.UTF8.GetBytes metódus visszafejti az UTF-16-ot string Unicode skaláris értékek sorozatába, majd újrakódolja ezeket a skaláris értékeket UTF-8-ra, és az eredményül kapott sorozatot egy byte tömbbe helyezi. A Encoding.UTF8.GetString metódus az ellenkező átalakítást hajtja végre, és UTF-8 byte tömböt konvertál UTF-16-rastring.

Figyelmeztetés

Mivel az UTF-8 köznapi az interneten, csábító lehet nyers bájtokat olvasni a vezetékből, és úgy kezelni az adatokat, mintha UTF-8 lenne. Azonban ellenőriznie kell, hogy valóban jól formázott-e. Előfordulhat, hogy egy rosszindulatú ügyfél rosszul formázott UTF-8-at küld a szolgáltatásnak. Ha úgy használja az adatokat, mintha azok megfelelően lettek volna formázva, az hibákat vagy biztonsági réseket okozhat az alkalmazásban. Az UTF-8-adatok érvényesítéséhez használhat egy olyan metódust, amely Encoding.UTF8.GetStringérvényesítést hajt végre, miközben a bejövő adatokat átalakítja egy string.

Jól formázott kódolás

A jól formázott Unicode-kódolás string olyan kódegységek, amelyek egyértelműen és hiba nélkül dekódolhatók Unicode skaláris értékek sorozatába. A jól formázott adatok szabadon átkódolhatók az UTF-8, az UTF-16 és az UTF-32 között.

Az a kérdés, hogy egy kódolási sorozat jól formázott-e vagy sem, nem függ a gép architektúrájának végiségétől. A rosszul formázott UTF-8 sorozat ugyanúgy alakul ki, mint a big endian és a little-endian gépeken.

Íme néhány példa a nem formázott kódolásokra:

  • Az UTF-8-ban a sorozat [ 6C C2 61 ] rosszul van formázva, mert C2 nem lehet követni .61

  • Az UTF-16-ban a szekvencia [ DC00 DD00 ] (vagy C#-ban) rosszul van formázva,"\udc00\udd00"string mert az alacsony helyettesítő DC00 nem követhető egy másik alacsony helyettesselDD00.

  • Az UTF-32-ben a sorozat [ 0011ABCD ] nem formázott, mert 0011ABCD kívül esik a Unicode skaláris értékeinek tartományán.

A .NET-ben a string példányok szinte mindig jól formázott UTF-16 adatokat tartalmaznak, de ez nem garantált. Az alábbi példák érvényes C# kódot mutatnak be, amely hibásan formázott UTF-16-adatokat hoz létre a példányokban string .

  • Egy rosszul formázott literál:

    const string s = "\ud800";
    
  • Egy helyettes párt felosztó alsztring:

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

Az API-k soha Encoding.UTF8.GetString nem adnak vissza rosszul formázott string példányokat. Encoding.GetString és Encoding.GetBytes a metódusok a bemenetben rosszul formázott sorozatokat észlelnek, és a kimenet létrehozásakor karakterhelyettesítést hajtanak végre. Ha például Encoding.ASCII.GetString(byte[]) nem ASCII bájt jelenik meg a bemenetben (az U+0000..U+007F tartományon kívül), a visszaadott string példányba egy "?" értéket szúr be. Encoding.UTF8.GetString(byte[]) a hibásan formázott UTF-8 sorozatokat U+FFFD REPLACEMENT CHARACTER ('�') a visszaadott string példányra cseréli. További információ : Unicode Standard, 5.22 és 3.9.

A beépített Encoding osztályok úgy is konfigurálhatók, hogy kivételt okozzanak ahelyett, hogy karakterhelyettesítést hajtanak végre, ha rosszul formázott sorozatok jelennek meg. Ezt a megközelítést gyakran használják olyan biztonsági szempontból érzékeny alkalmazásokban, ahol a karakterhelyettesítés nem feltétlenül elfogadható.

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

A beépített osztályok használatáról további információt a .NET karakterkódoló Encoding osztályainak használata című témakörben talál.

Lásd még