.NET içinde karakter kodlaması
Bu makale, char .NET tarafından kullanılan acter kodlama sistemlerine giriş sağlar. Makalede, , String , ve türlerinin Char Rune StringInfo Unicode, UTF-16 ve UTF-8 ile nasıl çalışaları açıklanmıştır.
Burada char acter terimi, bir okuyucunun tek bir görüntüleme öğesi olarak algıladığı genel anlamda kullanılır. Yaygın örnekler arasında "a" harfi, "@" simgesi ve emoji " 🐂 ". Bazen grapheme kümelerinin bölümünde açıklandıklarından, bir eylemci aslında birden çok bağımsız char görüntü öğeden oluşur.
ve string char türleri
Sınıfın bir örneği string bazı metinleri temsil eder. , string mantıksal olarak 16 bitlik değerlerin dizisidir ve her biri yapının bir char örneğidir. . string Length özelliği char örnekteki örnek sayısını string döndürür.
Aşağıdaki örnek işlev, bir içinde yer alan tüm örneklerin onaltılı olarak char yer alan değerlerini string yazdırır:
string"Hello" ifadesini bu işleve iletir ve aşağıdaki çıkışı elde etmek için:
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 char acter tek bir değerle temsil char edilen. Bu düzen, dünyanın dillerinin çoğu için geçerli olur. Örneğin, iki Çince eylemcinin çıkışı şu şekildedir: nǐ hǎo char merhaba:
PrintChars("你好");
"你好".Length = 2
s[0] = '你' ('\u4f60')
s[1] = '好' ('\u597d')
Ancak bazı diller ve bazı semboller ve emojiler için tek bir char acter'ı temsil etmek için iki char örnek gerekir. Örneğin, char Osage dilinde Osage anlamına gelen char sözcükte acter'ları ve örnekleri 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 char dışında her bir eylemci iki örnekle temsil char edildi.
Aşağıdaki örnekte bir ox emojisini gösteren gibi tek bir Unicode emojisi char de iki s ile temsil ediliyor:
"🐂".Length = 2
s[0] = '�' ('\ud83d')
s[1] = '�' ('\udc02')
Bu örnekler, örnek sayısını gösteren değerinin görüntülenen eylemci sayısını string.Length char göstermey char olduğunu gösterir. Tek bir char örnek tek başına bir acter'ı temsil etmek zorunda char değildir.
Tek char bir acter ile eşilen char çiftlere vekil çiftler adı verilmektedir. Nasıl olduklarını anlamak için Unicode ve UTF-16 kodlamasını anlamalısınız.
Unicode kod noktaları
Unicode, çeşitli platformlarda ve çeşitli dillerde ve betiklerde kullanım için uluslararası bir kodlama standardıdır.
Unicode Standardı, 1,1 milyondan fazla kod noktası tanımlar. Kod noktası, 0 ile U+10FFFF (ondalık 1.114.111) arasında bir tamsayı değeridir. Bazı kod noktaları harflere, sembollere veya emojiye atanır. Diğerleri, metin veya eylemcilerin nasıl görüntülendiğinden (örneğin yeni satıra ilerleyin) char denetim altına alan eylemlere atanır. Birçok kod noktası henüz atanmamıştır.
Burada, görünen Unicode ts bağlantılarına sahip kod noktası char atamalarına bazı örnekler verilmiştir:
| Ondalık | Onaltılık | Örnek | Description |
|---|---|---|---|
| 10 | U+000A |
Yok | SATıR BESLEME |
| 97 | U+0061 |
a | LATIN KÜÇÜK HARF A |
| 562 | U+0232 |
Ȳ | MAKRON ILE LATIN BÜYÜK HARFI Y |
| 68,675 | U+10C43 |
𐱃 | ESKI EDIC LETTER ORKHON AT |
| 127,801 | U+1F339 |
🌹 | ROSE emojisi |
Kod noktaları, söz dizimi kullanılarak özel olarak U+xxxx adlandırılır; burada, altılık kodlanmış xxxx tamsayı değeridir.
Tüm kod noktaları aralığı içinde iki alt düzen vardır:
- aralığındaki Temel Çok Dilde Düzlem (BMP).
U+0000..U+FFFFBu 16 bit aralık, dünyanın yazma sistemlerinin çoğunu kapsayacak kadar 65.536 kod noktası sağlar. - aralığındaki ek kod
U+10000..U+10FFFFnoktaları. Bu 21 bitlik aralık, daha az iyi bilinen diller ve emojiler gibi diğer amaçlar için kullanılan bir milyondan fazla ek kod noktası sağlar.
Aşağıdaki diyagramda BMP ile ek kod noktaları arasındaki ilişki göstermektedir.
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 char eylemci kodlama sistemidir. .NET, bir içinde metni kodlamak için UTF-16 string kullanır. Örnek, char 16 bit kod birimini temsil eder.
Tek bir 16 bit kod birimi, Temel Çok Dilde Düzlemin 16 bit aralığındaki herhangi bir kod noktasını temsil ediyor olabilir. Ancak ek aralıkta bir kod noktası için iki char örnek gerekir.
Vekil çiftler
İki 16 bit değerin tek bir 21 bit değere çevirisi, vekil kod noktaları (ondalık U+D800 U+DFFF 55.296 ile 57.343) arasında adlı özel bir aralık tarafından kolaylaştırıldı.
Aşağıdaki diyagramda BMP ile vekil kod noktaları arasındaki ilişki göstermektedir.
Yüksek yedek kod noktası ( ) hemen ardından düşük vekil kod noktası () geldiğinde, çift aşağıdaki formül kullanılarak ek kod noktası U+D800..U+DBFF olarak U+DC00..U+DFFF yorumlanır:
code point = 0x10000 +
((high surrogate code point - 0xD800) * 0x0400) +
(low surrogate code point - 0xDC00)
Ondalık basamak kullanan formül şu şekildedir:
code point = 65,536 +
((high surrogate code point - 55,296) * 1,024) +
(low surrogate code point - 56,320)
Yüksek yedek kod noktası, düşük yedek kod noktasından daha yüksek bir sayı değerine sahip değil. 20 bit kod noktası aralığının yüksek sıra 10 bitlerini hesaplamak için kullanılan yüksek yedek kod noktası "yüksek" olarak adlandırılan bir değerdir. Düşük yedek kod noktası, 10 bitlik düşük sırayı hesaplamak için kullanılır.
Örneğin, vekil çiftine karşılık gelen ve aşağıdaki gibi 0xD83C 0xDF39 hesaplanan gerçek kod noktası:
actual = 0x10000 + ((0xD83C - 0xD800) * 0x0400) + (0xDF39 - 0xDC00)
= 0x10000 + ( 0x003C * 0x0400) + 0x0339
= 0x10000 + 0xF000 + 0x0339
= 0x1F339
Ondalık basamak kullanılarak yapılan hesaplamanın aynısı şu şekildedir:
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 örnek, daha önce "\ud83c\udf39" bahsedilen kod noktasının UTF-16 U+1F339 ROSE ('🌹') kodlaması olduğunu gösteriyor.
Unicode skaler değerleri
Unicode skaler değeri terimi, vekil kod noktaları dışında tüm kod noktalarına başvurur. Başka bir deyişle skaler değer, bir acter atanmış veya gelecekte bir acter atanabilir herhangi char bir char kod noktasıdır. Burada "Karakter", bir kod noktasına atanabilir ve metin veya eylemcilerin nasıl görüntülenmiyor olduğunu kontrol altına alan eylemler gibi char şeyleri içerir.
Aşağıdaki diyagramda skaler değer kod noktaları göstermektedir.
RuneSkaler değer olarak tür
.NET Core 3.0'dan itibaren tür System.Text.Rune bir Unicode skaler değerini temsil eder. Rune.NET Core 2.x veya 4.x .NET Framework kullanılamaz.
RuneOluşturucular, sonuçta elde edilen örneğin geçerli bir Unicode skaler değer olduğunu doğrular, aksi takdirde bir özel durum oluştururlar. Aşağıdaki örnek, giriş geçerli skaler değerleri temsil ettiği Rune için örnekleri başarıyla örnekleyen kodu gösterir:
Aşağıdaki örnek bir özel durum oluşturur çünkü kod noktası vekil aralığındadır ve bir vekil çiftinin parçası değildir:
Kod noktası ek aralığın ötesinde olduğundan aşağıdaki örnek bir özel durum oluşturur:
Rune kullanım örneği: büyük/son harfi değiştirme
bir alan ve skaler değer olan bir kod noktasıyla çalıştığını varsayan BIR API, bir yedek çifti ise char char düzgün çalışmıyor. Örneğin, bir içinde her birini Char.ToUpperInvariant çağıran aşağıdaki yöntemi char string düşünün:
, input string küçük deseret harfini () içeriyorsa, bu er kod büyük 𐑉 harfe ( ) 𐐡 dönüştürmez. Kod, her char.ToUpperInvariant vekil kod noktasında ve üzerinde ayrı U+D801 U+DC49 çağrılar. Ancak tek başına küçük harf olarak tanımlamak için yeterli bilgiye sahip değildir, bu nedenle U+D801 char.ToUpperInvariant tek başına bırakır. Ayrıca aynı U+DC49 şekilde işlemeye de devam ediyor. Sonuç olarak, içinde '𐑉' küçük harf '𐐡' büyük input string harfe 𐐡.
Bir büyük harfe doğru şekilde dönüştürmek için iki string seçenek şunlardır:
String.ToUpperInvariant-by- string iterating yerine
chargirişte çağrısı.charYönteminstring.ToUpperInvarianther yedek çiftinin her iki parçasına da erişimi vardır, bu nedenle tüm Unicode kod noktalarını doğru şekilde işebilir.Aşağıdaki örnekte gösterildiği gibi Unicode skaler değerlerini örnekler yerine
Runecharörnek olarak yeniden deneyin. Örnek geçerli bir Unicode skaler değer olduğundan, skaler değer üzerinde çalışması bekilenRuneAPI'lere geçiril olabilir. Örneğin, aşağıdaki Rune.ToUpperInvariant örnekte gösterildiği gibi çağrısı doğru sonuçlar verir:
Diğer Rune API'ler
türü Rune çoğu API'nin analoglarını char ortaya çıkarır. Örneğin, aşağıdaki yöntemler statik API'leri türe char yansıtıyor:
Bir örnekten ham skaler değeri Rune almak için özelliğini Rune.Value kullanın.
Bir örneği Rune bir diziye geri dönüştürmek char için veya yöntemini Rune.ToString Rune.EncodeToUtf16 kullanın.
Herhangi bir Unicode skaler değeri tek bir veya bir vekil çiftle temsil edilebilir olduğu için, herhangi bir örnek en fazla char Rune 2 char örnekle temsil edilebilir. Bir Rune.Utf16SequenceLength örneği temsil etmek için kaç örneğin gerekli olduğunu görmek için char Rune kullanın.
.NET türü hakkında daha fazla Rune bilgi için bkz. Rune API başvurusu.
Grapheme kümeleri
Bir acter birden çok kod noktası birleşiminden kaynaklansa da genellikle "acter" yerine kullanılan daha açıklayıcı bir terim char char grapheme kümesidir. .NET'te eşdeğer terim, metin öğesidir.
string"a", "á", "á" ve " örneklerini 👩🏽🚒 düşünün. İşletim sisteminiz bunları Unicode standardı tarafından belirtilen şekilde ele alıyorsa, bu örneklerin her biri tek bir metin öğesi veya string grapheme kümesi olarak görünür. Ancak son ikisi birden fazla skaler değer kod noktasıyla temsil edildi.
string"a", bir skaler değerle temsil edilen ve bir örnek
chariçerir.U+0061 LATIN SMALL LETTER A
string"á", bir skaler değerle temsil edilen ve bir örnek
chariçerir.U+00E1 LATIN SMALL LETTER A WITH ACUTE
"á", "á" ile aynı görünüyor ancak iki skaler değerle temsil string edildi ve iki örnek
chariçeriyor.U+0061 LATIN SMALL LETTER AU+0301 COMBINING ACUTE ACCENT
Son olarak string , " " dört
👩🏽🚒skaler değerle temsil eder ve yedicharö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 JOINERU+1F692 FIRE ENGINE(ek aralık, vekil çift gerektirir)
Önceki örneklerden bazılarında (bir araya gelen vurgu değiştiricisi veya dış görünüm değiştiricisi gibi) kod noktası ekranda tek başına öğe olarak görüntülenmez. Bunun yerine, önünde gelen bir metin öğesinin görünümünü değiştirmeye hizmet verir. Bu örnekler, tek bir " acter" veya "grapheme kümesi" olarak kabul etmek için birden çok skaler değer char gerektirebileceklerini gösterir.
bir grapheme kümelerini listeley string için, aşağıdaki StringInfo örnekte gösterildiği gibi sınıfını kullanın. Swift'i biliyorsanız . NET türü StringInfo kavramsal olarak Swift'in türüne character benzer.
Örnek: char count Rune , ve text element instances
.NET API'lerde grapheme kümesine metin öğesi adı ve verir. Aşağıdaki yöntem, bir içinde char , ve metin öğesi örnekleri arasındaki Rune farkları string gösteriyor:
Bu kodu .NET Framework .NET Core 3.1 veya önceki bir sürümde çalıştırdıysanız emojinin metin öğesi sayısı olarak 4 gösterir. Bunun nedeni StringInfo sınıfındaki .NET 5'te düzeltilen bir hatadır.
Örnek: string örnekleri bölme
Örnekleri string bölerken vekil çiftleri ve grapheme kümelerini bölmekten kaçının. Bir içinde her 10 eylemciye satır sonu eklemek için aşağıdaki yanlış char kod örneğini göz önünde bulundurabilirsiniz: string
Bu kod örnekleri numaralandırı olduğundan, 10 sınırı katmana bölen bir vekil çift bölünecek ve bunlar arasına char char yeni bir satır gelecektir. Vekil kod noktaları yalnızca çift olarak anlamlı olduğundan, bu ekleme veri bozulmasına neden olur.
Örnekler yerine örnekleri (skaler değerler) numaralarsanız veri bozulması potansiyeli Rune ortadan char kaldırılmış değildir. Bir dizi Rune örnek, 10 sınırı katmana alan bir grapheme kümesi char oluşturur. Grapheme kümesi bölünmüşse, doğru yorumlandırılemez.
Aşağıdaki örnekte olduğu gibi grapheme kümelerini veya metin öğelerini sayarak bunu bozmak string daha iyi bir yaklaşımdır:
Ancak daha önce belirtildiği gibi , .NET 5 dışında .NET uygulamaları için sınıf bazı StringInfo grapheme kümelerini yanlış işleyebilir.
UTF-8 ve UTF-32
Yukarıdaki bölümlerde UTF-16'ya odaklanmıştık çünkü .NET örnekleri kodlamak için bunu string kullanır. Unicode için başka kodlama sistemleri de vardır : UTF-8 ve UTF-32. 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 biriminde herhangi bir skaler değeri temsil ediyor olabilir.
Aynı Unicode kod noktasının bu üç Unicode kodlama sistemi içinde nasıl temsil olduğunu 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, bir vekil çiftten 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 sıralı ise tek bir UTF-8 kod birimi kendisi için anlamsızdır.
Endianness
.NET'te, bir'in UTF-16 kod birimleri, 16 bit tamsayılar ( örnekler) dizisi olarak bitişik string char bellekte depolanır. Tek tek kod birimlerinin bitleri, geçerli mimarinin sonluluğuna göre ortaya çıkar.
Küçük bir endian mimarisinde, UTF-16 kod noktalerinden oluşan değeri, bayt olarak string [ D801 DCCC ] belleğe [ 0x01, 0xD8, 0xCC, 0xDC ] konabilir. Aynı büyük endian mimarisinde string bayt olarak belleğe atılacaktır. [ 0xD8, 0x01, 0xDC, 0xCC ]
Birbirleriyle iletişim kuran bilgisayar sistemleri, kablodan geçen verilerin temsili üzerinde fikir birliği olmalıdır. Çoğu ağ protokolü, metin iletilirken standart olarak UTF-8 kullanır ve kısmen küçük endian makinesiyle iletişim kuran büyük endian makinelerinden kaynaklanmayacak sorunları önlemek için kullanılır. UTF-8 kod noktalerinden oluşan, endianness ne olursa olsun her zaman bayt string [ F0 90 93 8C ] olarak temsil [ 0xF0, 0x90, 0x93, 0x8C ] eder.
.NET uygulamaları, metin ileterek UTF-8'i kullanmak için genellikle aşağıdaki örnekte olduğu gibi kod kullanır:
string stringToWrite = GetString();
byte[] stringAsUtf8Bytes = Encoding.UTF8.GetBytes(stringToWrite);
await outputStream.WriteAsync(stringAsUtf8Bytes, 0, stringAsUtf8Bytes.Length);
Önceki örnekte Encoding.UTF8.GetBytes yöntemi UTF-16 kodunu bir Unicode skaler değer dizisine geri döndürür, ardından bu string skaler değerleri UTF-8'e yeniden kodlar ve sonuçta elde edilen diziyi bir diziye byte yer verir. Encoding.UTF8.GetString yöntemi, bir UTF-8 dizisini byte UTF-16'ya dönüştürerek ters dönüştürmeyi string gerçekleştirir.
Uyarı
UTF-8 İnternet'te sıradan bir yer olduğu için, kablodan ham baytları okumak ve verileri UTF-8 gibi ele almak cazip olabilir. Ancak, gerçekten de iyi oluşturulmuş olduğunu doğrulamalı. Kötü amaçlı bir istemci, hizmetinize yanlış UTF-8 göndermektedir. Bu veriler üzerinde iyi oluşturulmuş gibi çalışırsanız, uygulamanıza hata veya güvenlik deliklerine neden olabilir. UTF-8 verilerini doğrulamak için, gelen verileri bir 'ye dönüştürürken doğrulama gerçekleştirecek Encoding.UTF8.GetString gibi bir yöntem string kullanabilirsiniz.
İyi formed kodlaması
İyi formed Unicode kodlaması, belirsiz ve hatasız bir şekilde Unicode skaler değerler dizisine çözülen kod string birimleridir. İyi oluşturulmuş veriler UTF-8, UTF-16 ve UTF-32 arasında serbestçe ve ileri geri koda sızabilirsiniz.
Bir kodlama dizisinin iyi biri olup olmadığı sorusu, bir makinenin mimarisinin endianlığıyla ilgili değildir. Kötü oluşturulmuş bir UTF-8 dizisi hem büyük endian hem de küçük endian makinelerde aynı şekilde kötü oluşturulmuştur.
Aşağıda, kötü oluşturulmuş kodlamalara bazı örnekler verilmiştir:
UTF-8'de, sırası tarafından
[ 6C C2 61 ]izlenilenemay nedeniyleC2kötü61oluştu.UTF-16'da, düşük vekil başka bir düşük vekil tarafından izleneyeneden dizi (veya C# içinde ) kötü
[ DC00 DD00 ]string"\udc00\udd00"DC00DD00oluşur.UTF-32'de, Unicode skaler değer aralığının dışında olduğundan dizi
[ 0011ABCD ]0011ABCDkötü oluşur.
.NET'te örnekler neredeyse her zaman iyi string UTF-16 verileri içerir, ancak bu garanti edilemez. Aşağıdaki örnekler, örneklerde kötü utF-16 verileri oluşturan geçerli C# string kodunu gösterir.
Kötü oluşturulmuş bir değişmez:
const string s = "\ud800";Yedek string çifti bölen bir alt:
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ü sıyrı string olmayan örnekler dönmez. Encoding.GetString ve Encoding.GetBytes yöntemleri, girişte kötü oluşturulmuş dizileri algılar ve char çıktıyı oluştururken acter değiştirme işlemi gerçekleştirin. Örneğin, girişte ASCII olmayan bir baytı Encoding.ASCII.GetString(byte[]) görüyorsa (U+0000..U+007F aralığının dışında), döndürülen örneğine bir '?' string ekler. Encoding.UTF8.GetString(byte[]) , döndürülen örnekteki kötü UTF-8 U+FFFD REPLACEMENT CHARACTER ('�') dizilerini ile string değiştirir. Daha fazla bilgi için bkz. Unicode Standardı,Bölüm 5.22 ve 3.9.
Yerleşik sınıflar, kötü oluşturulmuş diziler görülürken eylem değiştirme gerçekleştirmek yerine bir özel durum atacak Encoding char şekilde de yalıtabilirsiniz. Bu yaklaşım genellikle eylemci değiştirmenin kabul edilemey char olabileceği 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. Encoding char .NET'te acter kodlama sınıflarını kullanma.