.NET içinde karakter kodlaması

Bu makalede .NET tarafından kullanılan karakter kodlama sistemlerine giriş bilgileri sağlanmaktadır. Makalede, , String, Charve Rune türlerinin StringInfoUnicode, UTF-16 ve UTF-8 ile nasıl çalıştığı açıklanmaktadır.

Karakter terimi burada bir okuyucunun tek bir görüntü öğesi olarak algıladığı genel anlamda kullanılır. Yaygın örnekler arasında "a" harfi, "@" simgesi ve "🐂" emojisi yer alır. Bazen tek bir karaktere benzeyen şey, grapheme kümelerindeki bölümde açıklandığı gibi birden çok bağımsız görüntü öğesinden oluşur.

string ve char türleri

Sınıfın string bir örneği bazı metinleri temsil eder. A string mantıksal olarak 16 bit değerlerden oluşan bir dizidir ve bunların her biri yapının bir örneğidir char . string.Length özelliği, string örneğindeki char örneklerinin sayısını döndürür.

Aşağıdaki örnek işlev, tüm char örneklerinin içindeki string değerlerini onaltılık gösterimle yazdırır.

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 içerisine "Hello" metnini geçtiğinizde, aşağıdaki çıktıyı alırsınız:

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

Her karakter tek char bir değerle temsil edilir. Bu düzen, dünyanın çoğu dili için geçerlidir. Örneğin, nǐ hǎo olarak seslendirilen ve Hello anlamına gelen iki Çince karakterin çıkışı şu şekildedir:

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

Ancak, bazı diller ve bazı semboller ve emojiler için tek bir karakteri temsil etmek için iki char örnek gerekir. Örneğin, Osage dilinde Osage anlamına gelen sözcükteki karakterleri ve char örneklerini karşılaştırın.

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

Yukarıdaki örnekte, boşluk dışındaki her karakter iki char örnekle temsil edilir.

Aşağıdaki örnekte görüldüğü gibi bir öküz emojisini gösteren tek bir Unicode emojisi de iki charsn ile temsil edilir:

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

Bu örnekler, string.Length değerinin char örneklerinin sayısını gösterdiğini, ancak her zaman görüntülenen karakter sayısını temsil etmediğini gösterir. Tek char bir örnek tek başına bir karakteri temsil etmez.

Tek char bir karaktere eşleyen çiftlere vekil çiftler denir. Bunların nasıl çalıştığını anlamak için Unicode ve UTF-16 kodlamasını anlamanız gerekir.

Unicode kod noktaları

Unicode, çeşitli platformlarda ve çeşitli diller ve betiklerle kullanılmak üzere uluslararası bir kodlama standardıdır.

Unicode Standardı 1,1 milyondan fazla kod noktası tanımlar. Kod noktası, 0 U+10FFFF ile (ondalık 1.114.111) arasında bir tamsayı değeridir. Bazı kod noktaları harflere, simgelere veya emojilere atanır. Diğerleri, yeni satıra ilerleme gibi metin veya karakterlerin nasıl görüntüleneceğini denetleyebilen eylemlere atanır. Birçok kod noktası henüz atanmadı.

Burada, göründükleri Unicode grafiklerin bağlantılarını içeren kod noktası atamalarına bazı örnekler verilmiştir:

Ondalık Onaltılık Örnek Açıklama
10 U+000A Yok SATIR ATLAMA
97 U+0061 a LATIN KÜÇÜK A HARFI
562 U+0232 Ȳ MACRON IÇEREN LATIN BÜYÜK Y HARFI
68,675 U+10C43 𐱃 ESKİ TÜRK HARFİ ORHUN AT
127,801 U+1F339 🌹 GÜL emojisi

Kod noktalarına genellikle söz dizimi U+xxxxkullanılarak başvurulur; burada xxxx onaltılık kodlanmış tamsayı değeridir.

Kod noktalarının tam aralığı içinde iki alt yer vardır:

  • Temel Çok Dilli Düzlem (BMP) aralığındaki U+0000..U+FFFF. Bu 16 bitlik aralık, dünyanın yazma sistemlerinin çoğunu kapsayacak kadar 65.536 kod noktası sağlar.
  • Aralık içindeki ek kod noktalarıU+10000..U+10FFFF. Bu 21 bitlik aralık, daha az bilinen diller ve emojiler gibi diğer amaçlar için kullanılabilecek bir milyondan fazla ek kod noktası sağlar.

Aşağıdaki diyagramda BMP ile ek kod noktaları arasındaki ilişki gösterilmektedir.

BMP ve tamamlayıcı kod noktaları

UTF-16 kod birimleri

16 bit Unicode Dönüştürme Biçimi (UTF-16), Unicode kod noktalarını temsil etmek için 16 bit kod birimleri kullanan bir karakter kodlama sistemidir. .NET, içindeki stringmetni kodlamak için UTF-16 kullanır. Örnek char , 16 bit kod birimini temsil eder.

Tek bir 16 bit kod birimi, Temel Çok Dilli Düzlem'in 16 bit aralığındaki herhangi bir kod noktasını temsil edebilir. Ancak ek aralıktaki bir kod noktası için iki char örnek gerekir.

Vekil çiftler

İki adet 16 bit değerinin, vekil kod noktaları adı verilen özel bir aralık (ondalık 55,296 ile 57,343 arası, dahil) kullanılarak tek bir 21 bit değere çevrilmesi kolaylaştırılır.

Aşağıdaki diyagramda BMP ile vekil kod noktaları arasındaki ilişki gösterilmektedir.

BMP ve vekil kod noktaları

Yüksek vekil kod noktası () hemen ardından düşük vekil kod noktası () geldiğinde, çift aşağıdaki formül kullanılarak tamamlayıcı kod noktası olarak yorumlanır.

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

İşte ondalık gösterimi kullanan aynı formül:

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

Yüksek vekil kod noktası, düşük vekil kod noktasından daha yüksek bir sayı değerine sahip değildir. 20 bit kod noktası aralığının daha yüksek sıralı 10 bitini hesaplamak için kullanıldığından, yüksek vekil kod noktası "yüksek" olarak adlandırılır. Düşük vekil kod noktası, alt sıralı 10 bitleri hesaplamak için kullanılır.

Örneğin, 0xD83C ve 0xDF39 vekil çiftine karşılık gelen gerçek kod noktası aşağıdaki gibi hesaplanır:

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

Ondalık gösterimi kullanan aynı hesaplama aşağıdadır:

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

Yukarıdaki örnekte, daha önce bahsedilen kod noktasının U+1F339 ROSE ('🌹') UTF-16 kodlaması "\ud83c\udf39" olarak gösterilmektedir.

Unicode skaler değerleri

Unicode skaler değeri terimi, vekil kod noktaları dışındaki tüm kod noktalarına başvurur. Başka bir deyişle, skaler değer, bir karakter atanmış veya gelecekte bir karakter atanabilecek herhangi bir kod noktasıdır. Buradaki "Karakter", metin veya karakterlerin nasıl görüntüleneceğini denetleyen eylemler gibi öğeleri içeren bir kod noktasına atanabilen her şeyi ifade eder.

Aşağıdaki diyagramda skaler değer kod noktaları gösterilmektedir.

Skaler değerler

Rune Skaler değer olarak tür

Önemli

Türü Rune .NET Framework'te kullanılamaz.

.NET'te tür, System.Text.Rune Unicode skaler değerini temsil eder.

Oluşturucular Rune , sonuçta elde edilen örneğin geçerli bir Unicode skaler değeri olduğunu doğrular, aksi takdirde bir özel durum oluştururlar. Aşağıdaki örnekte, giriş geçerli skaler değerleri temsil ettiğinden örnekleri başarıyla oluşturan Rune kod gösterilmektedir:

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

Aşağıdaki örnek, kod noktası vekil aralığında olduğundan ve bir vekil çiftin parçası olmadığından bir istisna oluşturur.

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

Aşağıdaki örnek, kod noktası tamamlayıcı aralığın ötesinde olduğundan bir özel durum oluşturur:

Rune g = new Rune(0x12345678);

Rune kullanım örneği: harflerin büyük/küçük harf durumunu değiştirme

Bir char alan ve skaler bir kod noktası üzerinde çalıştığını varsayan bir API, char bir vekil çiftinden geliyorsa düzgün çalışmaz. Örneğin, string içindeki her char üzerinde Char.ToUpperInvariant çağıran aşağıdaki yöntemi göz önünde bulundurun:

// 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 Deseret küçük harfi ()er𐑉 taşırsa, kod bunu büyük harfe (𐐡) dönüştürmez. Kod, char.ToUpperInvariant her vekil kod noktasında ve U+D801üzerinde ayrı olarak çağrılarU+DC49. Ancak U+D801 , küçük harf olarak tanımlamak için tek başına yeterli bilgiye sahip değildir, bu nedenle char.ToUpperInvariant onu yalnız bırakır. Ve aynı şekilde işler U+DC49 . Sonuç olarak, inputstring içindeki küçük harf '𐑉' büyük harf '𐐡'ye dönüştürülmez.

string'yi büyük harfe dönüştürmenin iki doğru yolu şunlardır:

  • Girdiyi string çağırın String.ToUpperInvariant, charchar yinelemesi yerine. string.ToUpperInvariant yöntemi, her vekil çiftin her iki bölümüne de erişebilir, bu nedenle tüm Unicode kod noktalarını doğru şekilde işleyebilir.

  • Unicode skaler değerlerin char örnekleri yerine, Rune örnekleri olarak yinelenmesi gerektiğini aşağıdaki örnek göstermektedir. Örnek Rune geçerli bir Unicode skaler değeri olduğundan, skaler değer üzerinde çalışması beklenen API'lere geçirilebilir. Örneğin, aşağıdaki örnekte gösterildiği gibi çağırma Rune.ToUpperInvariant doğru sonuçlar verir:

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

Diğer Rune API'ler

Türü, Rune API'lerin birçoğunun char analoglarını ortaya çıkarır. Örneğin, aşağıdaki yöntemler tür char üzerindeki statik API'leri yansıtır.

Bir Rune örnekten ham skaler değeri almak için özelliğini kullanın Rune.Value .

Rune örneğini tekrar char dizisine çevirmek için Rune.ToString veya Rune.EncodeToUtf16 yöntemini kullanın.

Herhangi bir Unicode skaler değeri tek char bir veya vekil çift tarafından temsil edilebilir olduğundan, herhangi bir Rune örnek en fazla 2 char örnekle temsil edilebilir. Rune.Utf16SequenceLength kullanarak bir Rune örneğini temsil etmek için kaç char örneği gerektiğini görün.

.NET Rune türü hakkında daha fazla bilgi için bkz Rune . API başvurusu.

Grapheme kümeleri

Bir karaktere benzeyen şey birden çok kod noktası birleşiminden kaynaklanabilir, bu nedenle genellikle "karakter" yerine kullanılan daha açıklayıcı bir terim grapheme kümesidir. .NET'teki eşdeğer terim metin öğesidir.

string"a", "á", "á" ve "👩🏽‍🚒" örneklerini göz önünde bulundurun. İşletim sisteminiz bunları Unicode standardı tarafından belirtilen şekilde işlerse, bu string örneklerin her biri tek bir metin öğesi veya grapheme kümesi olarak görünür. Ancak son ikisi birden fazla skaler değer kod noktasıyla temsil edilir.

  • string "a" bir skaler değerle temsil edilir ve bir char örnek içerir.

    • U+0061 LATIN SMALL LETTER A
  • string "á" bir skaler değerle temsil edilir ve bir char örnek içerir.

    • U+00E1 LATIN SMALL LETTER A WITH ACUTE
  • "á", string "á" ile aynı görünür, ancak iki skaler değerle temsil edilir ve iki char örnek içerir.

    • U+0061 LATIN SMALL LETTER A
    • U+0301 COMBINING ACUTE ACCENT
  • Son olarak, string "👩🏽‍🚒" dört skaler değerle temsil edilir ve yedi char örnek içerir.

    • U+1F469 WOMAN (ek aralık, vekil çift gerektirir)
    • U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (ek aralık, vekil çift gerektirir)
    • U+200D ZERO WIDTH JOINER
    • U+1F692 FIRE ENGINE (ek aralık, vekil çift gerektirir)

Önceki örneklerden bazılarında (örneğin, birleşen vurgu değiştirici veya dış görünüm tonu değiştiricisi) kod noktası ekranda tek başına bir öğe olarak görüntülenmez. Bunun yerine, ondan önce gelen bir metin öğesinin görünümünü değiştirmeye hizmet eder. Bu örnekler, tek bir "karakter" veya "grapheme kümesi" olarak düşündüğümüz şeyi oluşturmak için birden çok skaler değerin gerekebileceğini göstermektedir.

bir stringöğesinin grapheme kümelerini listelemek için aşağıdaki örnekte gösterildiği gibi sınıfını kullanın StringInfo . Swift'i biliyorsanız .NET StringInfo türü kavramsal olarak Swift'in character türüne benzer.

Örnek: char, Rune ve metin öğesi örneklerini sayın

.NET API'lerinde grapheme kümesine metin öğesi adı verilir. Aşağıdaki yöntem, içindeki char, Runeve metin öğesi örnekleri arasındaki stringfarkları gösterir:

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

Örnek: string örneklerini bölme

Örnekleri bölerken string vekil çiftleri ve grapheme kümelerini bölmekten kaçının. aşağıdaki hatalı kod örneğini düşünün. Bu örnek, içindeki her 10 karakterde bir stringsatır sonları eklemeyi amaçlıyor:

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

Bu kod char örneklerini numaralandırdığı için, char sınırındaki bir 10 bölgesinde çift olarak duran bir vekil çift bölünecek ve aralarına yeni bir satır eklenecektir. Yedek kod noktaları yalnızca çiftler olarak anlamlı olduğundan bu ekleme veri bozulmasına neden olur.

Veri bozulması olasılığı, char örnekleri yerine Rune örneklerini (skaler değerler) numaralandırırsanız ortadan kaldırılamaz. Bir örnek kümesi Rune , 10char sınırına sahip bir grapheme kümesi oluşturur. Grapheme kümesi bölünmüşse, doğru yorumlanamaz.

Daha iyi bir yaklaşım, aşağıdaki örnekte olduğu gibi grafeme kümelerini veya metin öğelerini sayarak bölmektir 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();

}

Daha önce belirtildiği gibi, .NET 5'in StringInfo öncesinde sınıfta bazı grapheme kümelerinin yanlış işlenmesine neden olan bir hata vardı.

UTF-8 ve UTF-32

Yukarıdaki bölümler UTF-16'ya odaklanmıştır çünkü .NET örnekleri kodlamak string için bunu kullanır. Unicode - UTF-8 ve UTF-32 için başka kodlama sistemleri de vardır. Bu kodlamalar sırasıyla 8 bit kod birimlerini ve 32 bit kod birimlerini kullanır.

UTF-16 gibi UTF-8 de bazı Unicode skaler değerleri temsil etmek için birden çok kod birimi gerektirir. UTF-32, tek bir 32 bit kod birimindeki herhangi bir skaler değeri temsil edebilir.

Bu üç Unicode kodlama sisteminin her birinde aynı Unicode kod noktasının nasıl gösterildiğini gösteren bazı örnekler aşağıda verilmiştir:

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)

Daha önce belirtildiği gibi, vekil çiftten alınan tek bir UTF-16 kod birimi tek başına anlamsızdır. Aynı şekilde, tek bir UTF-8 kod birimi, skaler değeri hesaplamak için kullanılan iki, üç veya dört bir dizideyse tek başına anlamsızdır.

Not

C# 11 ile başlayarak, bir literal üzerinde "u8" sonekini kullanarak UTF-8 literal'lerini temsil edebilirsiniz. UTF-8 string değişmez değerleri hakkında daha fazla bilgi için C# Kılavuzu'ndaki yerleşik başvuru türleriylestringmakalenin "değişmez değerler" bölümüne bakın.

Bayt Sırası

.NET'te, bir string öğesinin UTF-16 kod birimleri, 16 bit tamsayıların (char örnekler) dizisi olarak bitişik bellekte depolanır. Tek tek kod birimlerinin bitleri, geçerli mimarinin bayt sıralaması doğrultusunda düzenlenir.

Little-endian mimaride, UTF-16 kod noktalarından [ D801 DCCC ] oluşan string, bellekte [ 0x01, 0xD8, 0xCC, 0xDC ] baytları olarak düzenlenir. Büyük endian mimaride aynı string, bellekte [ 0xD8, 0x01, 0xDC, 0xCC ] baytları olarak yerleştirilir.

Birbirleriyle iletişim kuran bilgisayar sistemleri, verilerin kablodan geçen temsili konusunda anlaşmalıdır. Çoğu ağ protokolü, metin aktarırken standart olarak UTF-8 kullanır; bu, büyük-endian ve little-endian makineler arasında iletişim kurarken ortaya çıkabilecek sorunlardan kaçınmak içindir. string UTF-8 kod noktalarından oluşan [ F0 90 93 8C ], endianstan bağımsız olarak [ 0xF0, 0x90, 0x93, 0x8C ] baytları olarak temsil edilir.

UtF-8'i metin iletiminde kullanmak için .NET uygulamaları genellikle aşağıdaki örneğe benzer bir kod kullanır:

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

Yukarıdaki örnekte, Encoding.UTF8.GetBytes yöntemi UTF-16 kodlamasını tekrar bir dizi Unicode doğrudan değerine çözer, ardından bu değerleri UTF-8'e yeniden kodlar ve ortaya çıkan diziyi bir byte dizisine yerleştirir. Encoding.UTF8.GetString yöntemi ters dönüştürme gerçekleştirir ve utf-8 byte dizisini UTF-16'ya stringdönüştürür.

Uyarı

UTF-8 İnternet'te yaygın olduğundan, kablodan ham baytları okumak ve verileri UTF-8 gibi işlemek cazip olabilir. Ancak, gerçekten iyi biçimlendirilmiş olduğunu doğrulamanız gerekir. Kötü amaçlı bir istemci, hizmetinize kötü biçimlendirilmiş UTF-8 gönderebilir. Bu verileri sanki iyi biçimlendirilmiş gibi işlerseniz, uygulamanızda hatalara veya güvenlik açıklarına yol açabilir. UTF-8 verilerini doğrulamak için, gelen verileri bir string'e dönüştürürken doğrulama gerçekleştiren Encoding.UTF8.GetString gibi bir yöntem kullanabilirsiniz.

İyi biçimlendirilmiş kodlama

İyi biçimlendirilmiş Unicode kodlaması, string kesin olmayan ve hatasız olarak unicode skaler değerler dizisine çözülebilen kod birimlerinden oluşan bir kod birimidir. İyi biçimlendirilmiş veriler UTF-8, UTF-16 ve UTF-32 arasında serbestçe kodlanabilir.

Kodlama dizisinin iyi biçimlendirilmiş olup olmadığı sorusu, bir makinenin mimarisinin son durumuyla ilişkili değildir. Kötü biçimlendirilmiş UTF-8 dizisi hem büyük hem de küçük endian makinelerde aynı şekilde şekilsizdir.

Aşağıda, kötü biçimlendirilmiş kodlama örnekleri verilmiştir:

  • UTF-8'de, C261 izlenemediği için [ 6C C2 61 ] dizisi yanlış biçimlendirilmiştir.

  • UTF-16'da, sıra [ DC00 DD00 ] (veya C# dilinde string"\udc00\udd00") kötü biçimlendirilmiştir çünkü düşük vekil DC00, başka bir düşük vekil DD00 tarafından izlenemez.

  • UTF-32'de, [ 0011ABCD ] dizisi, 0011ABCD Unicode skalar değerler aralığının dışında olduğundan kötü biçimlendirilmiştir.

.NET'te string örnekler neredeyse her zaman iyi biçimlendirilmiş UTF-16 verileri içerir, ancak bu garanti değildir. Aşağıdaki örneklerde string kötü biçimlendirilmiş UTF-16 verileri oluşturan geçerli C# kodu gösterilmektedir.

  • Kötü biçimlendirilmiş bir sabit:

    const string s = "\ud800";
    
  • Unicode vekil çifti bölen bir alt dize.

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

Gibi Encoding.UTF8.GetString API'ler hiçbir zaman kötü biçimlendirilmiş string örnekler döndürmez. Encoding.GetString ve Encoding.GetBytes yöntemleri girişteki hatalı biçimlendirilmiş dizileri algılar ve çıkışı oluştururken karakter değişimi gerçekleştirir. Örneğin, Encoding.ASCII.GetString(byte[]) girişte ASCII olmayan bir bayt görürse (U+0000..U+007F aralığının dışında), döndürülen string örneğe bir '?' ekler. Encoding.UTF8.GetString(byte[]) hatalı biçimlendirilmiş UTF-8 dizilerini, döndürülen bu string örnekte U+FFFD REPLACEMENT CHARACTER ('�') ile değiştirir. Daha fazla bilgi için bkz . Unicode Standart, Bölüm 5.22 ve 3.9.

Yerleşik Encoding sınıflar, hatalı biçimlendirilmiş diziler görüldüğünde karakter değiştirme gerçekleştirmek yerine bir hata fırlatacak şekilde de yapılandırılabilir. Bu yaklaşım genellikle karakter değişiminin kabul edilemeyebilir olduğu, güvenlik duyarlı uygulamalarda kullanılır.

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

Yerleşik sınıfları kullanma hakkında bilgi için bkz. .NET'te Encodingkarakter kodlama sınıflarını kullanma.

Ayrıca bkz.