Teckenkodning i .NET
Den här artikeln innehåller en introduktion till teckenkodningssystem som används av .NET. Artikeln förklarar hur typerna String, Char, Runeoch StringInfo fungerar med Unicode, UTF-16 och UTF-8.
Termen tecken 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 ett tecken 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 tecken 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 tecken 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 ett enda tecken. Jämför till exempel tecknen och char
instanserna 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 tecken utom utrymmet av två char
instanser.
En enda Unicode-emoji representeras också av två char
s, 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.Length
för , som anger antalet char
instanser, inte nödvändigtvis anger antalet tecken som visas. En enskild char
instans representerar inte nödvändigtvis ett tecken.
De char
par som mappas till ett enda tecken 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 tecken 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-diagram dä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.
UTF-16-kodenheter
16-bitars Unicode Transformation Format (UTF-16) är ett teckenkodningssystem 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.
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 ett tecken eller som kan tilldelas ett tecken 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 tecken visas.
Följande diagram illustrerar de skalära värdekodpunkterna.
Typen Rune som ett skalärt värde
Viktigt!
Typen Rune
är inte tillgänglig i .NET Framework.
I .NET System.Text.Rune representerar typen ett Unicode-skalärvärde.
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 input
string 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 input
string 𐑉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
. Metodenstring.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örchar
instanser, som du ser i följande exempel. Eftersom enRune
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 char
s 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 ett tecken kan bero på en kombination av flera kodpunkter, så en mer beskrivande term som ofta används i stället för "tecken" ä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 sjuchar
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 ett enda "tecken" eller "grapheme-kluster".
Om du vill räkna upp grapheme-kluster i en string
anvä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
, Rune
och 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
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 10:e tecken 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)
{
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 eftersomC2
den inte kan följas av61
.I UTF-16 är sekvensen
[ DC00 DD00 ]
(eller, i C#, string"\udc00\udd00"
) dåligt utformad eftersom den låga surrogatenDC00
inte kan följas av en annan låg surrogatDD00
.I UTF-32 är sekvensen
[ 0011ABCD ]
dåligt utformad eftersom0011ABCD
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 delsträng 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 teckenersä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 teckenersättning när oformaterade sekvenser visas. Den här metoden används ofta i säkerhetskänsliga program där teckenersä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 teckenkodningsklasser i .NET.