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 char
s 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.Length
nem 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+xxxx
haszná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+10FFFF
kiegé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.
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.
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.
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 input
string 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 input
string "𐑉" 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
char
helyett - by-char
.string Astring.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éntRune
kell iterálni. Mivel aRune
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étchar
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 string
adott 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 char
kü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, mertC2
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, mert0011ABCD
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.