Sanyanggae LogoSanyanggae

💻 Web ve ön yüz geliştirme

Unicode ile UTF-8 nasıl ayrılır? Bozuk Korece, baytlar, dize uzunluğu ve normalleştirme

Korece ve emojilerin kod noktalarını, UTF-8 baytlarını ve JavaScript dize uzunluğunu karşılaştırın; yanlış çözümlemeyi, yazı tipi sorunlarını, normalleştirmeyi ve özgün metni korumayı ayırın.

사냥개 (Sanyanggae)Yayımlanma Güncelleme 16 dakika

Bu, Korece özgün metnin çevirisidir. Korece özgün metin · Çeviri incelemesi: Yapay zekâ incelemesi

Karakter ve kod noktasını, UTF-8 baytlarını, gösterim ve normalleştirmeyi üç kutuyla ilişkilendiren açıklama şeması
Unicode karakter numarası, UTF-8 saklama baytları ve gösterim-karşılaştırma kurallarını ayıran özgün açıklama şemasıdır; gerçek arıza ekranı veya ölçüm değildir.

1. Karakterin numarası ile saklanan baytlar farklıdır

Dışa aktardığınız Korece belgeyi yeniden açınca başlığın tuhaf karakterlere dönüştüğünü düşünün. Yazanın dili değişmemiştir; saklanan sayıları okuma kuralları uyuşmamış olabilir. Unicode, karakterlere ve simgelere kod noktası denen numaralar veren bir standarttır; UTF-8 ise bunlara karşılık gelen değerleri bir bayt dizisiyle gösteren kodlamadır. Korece ‘가’ hecesinin kod noktası U+AC00'dır. U+ sonrasındaki gösterim onaltılıktır; dosyadaki sıra numarasını veya bellek adresini belirtmez. Bu numara yazı tipinin biçimini veya ekrandaki piksel konumunu da belirlemez. Aynı kod noktası, yazı tipi ve dizgi ortamına göre farklı çizilebilir. Unicode desteği, UTF-8 olarak kaydetme ve Korece yazı tipi kurma birbiriyle ilişkili olsa da ayrı ayrı incelenmelidir. Unicode'un UTF açıklaması, karakteri saklama biçiminden ayırmak için bir başlangıçtır.

UTF-8'de ‘가’, onaltılık EA B0 80 biçimindeki üç bayttır. İngilizce A ise U+0041'dir ve UTF-8'de tek bir 41 baytıyla gösterilir. Bir bayt sekiz bittir; aynı belgedeki karakterlerin bayt uzunlukları değişebilir. .txt veya .csv uzantısı da gerçek kodlamayı tek başına belirlemez. UTF-8, geçerli Unicode skaler değerlerini bir ile dört bayt arasında gösterdiğinden, görünen karakter sayısını sabit bir sayıyla çarparak dosya boyutunu kesin hesaplayamazsınız. Bu Korece hece örneğini bütün Korece simgelere veya tüm dillere genellememelisiniz. Kodlama, karakter bilgisini baytlara taşıyan bir kuraldır; sıkıştırma veya şifreleme değildir. Aynı cümlenin toplam boyutu, dosya biçiminin ek bilgileri, satır sonları ve aktarım sıkıştırmasıyla değişebilir. WHATWG Encoding Standard içindeki bayt-karakter dönüşümünü, dosyanın toplam boyutundan ve gizlilik işlevlerinden ayırmak sorunu daraltır.

2. Bayt, kod birimi ve görünen karakter sayısı

JavaScript'te bir dizenin length değeri UTF-16 kod birimlerini sayar. Bu sayı, UTF-8 dosyasının bayt sayısı veya kişinin algıladığı karakter sayısı değildir. ‘😀’ yüzü tek bir U+1F600 kod noktasıdır; ancak iki UTF-16 kod birimi kullandığından '😀'.length değeri 2'dir. UTF-8 karşılığı F0 9F 98 80, yani dört bayttır. codePointAt(0) ile elde edilen ondalık 128512, U+1F600 ile aynı numaradır; karakter sayısı veya dosya boyutu değildir. ‘가’ ise bir kod noktası, bir UTF-16 kod birimi ve üç UTF-8 baytıdır. Aynı dizeden farklı sayılar çıkmasının nedeni ölçü birimleridir. MDN'nin dize uzunluğu açıklaması, length'in birimini belirtir. Bu sayıdan JavaScript motorunun gerçekte kaç bayt bellek kullandığı da çıkarılamaz.

Kod noktalarını tek tek saysanız da sonuç her zaman kullanıcının gördüğü karakter sayısıyla aynı olmaz. Temel karaktere birleşen işaretler eklenebilir veya bir emoji birden çok kod noktasından oluşabilir. Unicode metin bölümleme kuralları, genişletilmiş grafem kümelerinin sınırlarını açıklar. Array.from(text) gibi kod noktalarıyla ilerleyen bir yöntem, birkaç kod noktasının oluşturduğu kümeleri kendiliğinden saymaz. Tarayıcıda bunları saymak için ortam desteğini kontrol edip Intl.Segmenter seçeneği granularity'yi 'grapheme' olarak belirlemeyi düşünebilirsiniz. Ancak grafem kümesi, yazı tipinin çizdiği tek şekille bire bir eşleşmek zorunda değildir; dillerin düzenleme beklentileri de farklı olabilir. Tanıtım alanında kullanıcıya anlamlı bir karakter sınırı, depolama ve iletişim içinse ayrı bayt sınırları kullanın. Ekranda ‘20 karakter’ yazarken içeride yalnızca UTF-16 kod birimlerini kesmek, emoji veya birleşen karakterleri ortasından bölebilir.

3. Bozuk Korecede saklamayı, iletimi ve gösterimi ayırın

Baytlar yazılırken kullanılan kodlama ile okunurken kullanılan çözümleme uyuşmazsa karakterler bozulabilir. UTF-8 dosyasını başka bir kodlamayla okumak, dosyanın kendisi değişmese bile ekranda yanlış karakterler gösterebilir. HTML'de gerçek saklama biçimini, HTTP Content-Type yanıtındaki charset'i ve belgenin başındaki meta charset bildirimini birlikte kontrol edin. HTML'deki lang='ko', içeriğin dilini bildirir; Korece baytların hangi kodlamayla okunacağını belirten charset'ten farklıdır. W3C'nin kodlama bildirimi rehberi, yalnızca bildirimi UTF-8 yapmayı değil, dosyayı gerçekten UTF-8 kaydetmeyi ve sunucu ile belge bildirimlerini eşleştirmeyi anlatır. Kodlama imzaları, belirleme önceliğini de etkileyebilir; meta öğesinin her zaman bütün diğer bilgileri geçersiz kıldığını düşünmeyin. Dosya doğrudan açılınca ve sunucudan alınca farklı görünüyorsa iletim ayarları önemli bir ipucudur.

Gerekli glif yazı tipinde bulunmadığı için görünen kare kutuların nedeni, yanlış çözümlemeden farklı olabilir. Veri doğru olduğu hâlde çizilecek biçim bulunamıyorsa saklama kodlamasını değiştirmek çözüm olmaz. Tersine, başka karakterler olarak çözümlenmiş metni yeni bir yazı tipiyle göstermek de özgün karakterleri geri getirmez. İncelemede sorunlu dosyanın kopyasını koruyun; düzenleyicinin seçtiği kodlamayı, sunucu bildirimini, tarayıcının çözdüğü karakterleri ve yazı tipini sırayla karşılaştırın. Yalnızca görünüme dayanmayın; aynı bölümü kopyalayınca hangi kod noktalarının geldiğini de kontrol edin. Yalnızca bir tarayıcıda mı farklı olduğunu, başka düzenleyicilerin de aynı dosyayı yanlış mı okuduğunu kaydetmek, gösterim sorunu ile veri değişikliğini ayırır. Doğru karakterler çıkana kadar tekrar tekrar kaydetmek özgün baytları kaybettirebilir. Bildirimler doğruyken yalnızca belirli bir gönderi bozuksa, içeriğin veritabanına girmeden önce hangi aşamada değiştiğini de araştırın.

4. Aynı değerleri TextEncoder ve TextDecoder ile incelemek

Basit denemeye kendinizin oluşturduğu kısa bir dizeyle başlayın. TextEncoder, JavaScript dizesini UTF-8 bayt dizisine çevirir. new TextEncoder().encode('가') sonucunu onaltılık okuyunca EA B0 80 görülür ve dizinin length değeri 3'tür. Geliştirme araçları aynı değerleri ondalık 234, 176, 128 olarak gösterebilir; bu başka veri değil, sayı tabanının farkıdır. Bunu normal dize length değerinin 1 olmasıyla karşılaştırmak birimleri açığa çıkarır. Diziyi new TextDecoder('utf-8').decode(bytes) ile okuyunca ‘가’ elde edilir; burada bytes, az önce oluşturulan bayt dizisidir. Bu kodlayıcı istenen herhangi bir eski kodlamaya kaydeden genel dönüştürücü değil, UTF-8 üreten bir araçtır. Gerçek dosyayı incelerken önce özgün baytları kullanın. Yanlış okunmuş ekran metnini yeniden UTF-8'e kodlamak, ilk dosyanın hangi baytlardan oluştuğunu kanıtlamaz.

TextDecoder kurucusunda 'utf-8' ve { fatal: true } belirtilirse, kurala uymayan girdi sessizce değiştirilmek yerine hataya yol açabilir. Örneğin tek FF baytı geçerli UTF-8 olmadığından bu kipte çözümleme başarısızdır. Varsayılan değiştirme kipinde hatalı bölüm U+FFFD değiştirme karakteri olabilir. Fakat hata olmaması, özgün kodlamanın bulunduğunu veya içeriğin doğrulandığını göstermez. Bazı baytlar birden çok kodlamada geçerlidir; daha önce yanlış karakterlere dönmüş metin de yeniden geçerli UTF-8 olarak saklanmış olabilir. Bu denetim, seçilen çözücünün girdiyi kabul edip etmediğini sınar. Belgenin kaynağını, özgün metinle eşleşmesini veya dosyanın arada değiştirilmediğini kendiliğinden doğrulamaz.

5. Normalleştirme, eşdeğer gösterimleri uzlaştırır

Doğru okunmuş iki dizenin iç kod noktası dizileri farklı olabilir. ‘가’, tek U+AC00 ile veya birleşen başlangıç ünsüzü U+1100 ve ünlü U+1161 sırasıyla gösterilebilir. Son iki değer, günlük uyumluluk jamosu ‘ㄱ’ ve ‘ㅏ’ karakterlerini yan yana yazmakla aynı değildir; numaraları da kontrol edin. İki gösterim genellikle aynı heceyi çizer, fakat JavaScript'in ham dize karşılaştırmasında eşit olmayabilir. Unicode normalleştirme kurallarına göre her ikisine NFC uygulamak, bu kanonik olarak eşdeğer örneği aynı gösterime getirir. NFD, kanonik ayrıştırma uygular. Bu örnekte NFC'nin UTF-8 gösterimi üç, NFD'deki iki jamo ise altı bayttır; aynı görünümün saklama boyutu farklı olabilir. Arama sorgusu ile saklanan ad aynı görünürken eşleşmiyorsa bu fark araştırılabilir. Bunu baytların yanlış yorumlanmasından doğan karakter bozulmasından ayırın.

Normalleştirme biçimi de amaca göre seçilir. NFC kanonik eşdeğerliği ele alırken NFKC uyumluluk ayrıştırmasını da içerir; tam genişlikli veya daire içindeki rakamlar gibi ayrı girilmiş gösterimleri daha geniş biçimde değiştirebilir. Arama için karşılaştırma kopyası üretirken bu yararlı olabilir; fakat matematiksel gösterimde veya özgün belgede fark anlamlı olabilir. Bu yüzden özgün değeri korumak ve gereken karşılaştırma kopyasına hangi biçimin uygulanacağını belirlemek yararlıdır. Normalleştirme büyük/küçük harf karşılaştırmasının, dile özgü sıralamanın veya eşsesli sözcük bulmanın da yerine geçmez. Mevcut dosya adlarını veya hesap kimliklerini topluca dönüştürmek ayrı öğeleri aynı karşılaştırma değerinde birleştirebilir; önce adlandırma kurallarını ve çakışma işlemlerini inceleyin. Benzer görünen bütün karakterleri aynı değerle saklamak, normalleştirmenin genel amacı değildir.

6. Özgün metni koruyup dönüşüm sınırlarını kaydedin

Bozulmanın nedenini araştırırken ilk dosyayı üzerine yazmadan önce özgün baytları ve edinme yolunu koruyun. Kopyayı beklenen kodlamayla okuyun ve doğru görünen sonucu yeniden kaydedip açınca aynı içeriğin kaldığını kontrol edin. W3C'nin BOM açıklamasında anlatılan UTF-8 başlangıç imzası EF BB BF'dir ve zorunlu değildir. UTF-8'de bayt sırası seçilmediğinden bu imzayı UTF-16'nın bayt sırasını seçmekle aynı işlevde düşünmeyin. BOM eklemek veya kaldırmakla gövde baytlarını başka kodlamaya dönüştürmek de farklıdır. Okunamayan değerler değiştirme karakterine veya soru işaretine dönüp kaydedildiyse birden çok özgün değer tek değerde birleşmiş olabilir; otomatik kurtarma garanti değildir. Ancak özgün kodlama ve dönüşüm geçmişi biliniyorsa tersine çevirme olasılığı değerlendirilebilir; normalleştirme kaybolmuş baytları kurtaran araç değildir.

Çok dilli özellikleri yalnızca tek İngilizce satırla sınamayın. Kısa örnek grubuna Korece heceyi, birleşen işaretli Latin karakterlerini, ek düzlem emojilerini ve Arapça ya da İbranice gibi sağdan sola metni katıp girdi, saklama, yeniden okuma ve dışa aktarmayı karşılaştırın. Her aşamada görünümü, kod noktalarını, UTF-8 bayt sayısını ve ürünün belirlediği karakter sayımı birimini inceleyin. Okuma yönü UTF-8'i başka kodlamaya çevirerek düzeltilmez; arayüzün dili ve yön ayarları ayrıca incelenir. Girdi sınırını veya arama karşılaştırmasını değiştirirken de özgün metni koruyup yeni ölçütü açıklayın. Tarayıcıdaki sayacın ve sunucunun kabul sınırının aynı birimi kullanıp kullanmadığını kontrol edin; farklı sınırlar gerekiyorsa ikisini de gösterin. Buradaki sayılar ve kodlar kısa örnek dizelerin dönüşümleridir; belirli bir hizmet arızasının ölçümü değildir. Karakter numarası, saklanan bayt, dize birimi, gösterim ve karşılaştırma kurallarını sırayla incelemek her sınırda neyin değiştiğini yeniden üretilebilir kılar.

Kanıtlar ve kaynaklar

Bu yazı kamuya açık kaynaklara dayanır. Bir kuruluşun kaynak gösterilmesi, yazıyı incelediği veya onayladığı anlamına gelmez.

Yayıncı hakkında ve iletişim

Aynı konudaki diğer yazılar