İçeriğe geç
academia.sh

Ders 06 / 16

Karakter Kodlamaları

ASCII'den Unicode'a karakter kümeleri, kod noktası ile bayt dizisi ayrımı ve UTF-8 kodlaması.

İçindekiler

Sayılar için temsil kuralları donanımın yapısından türetilebiliyordu: konum değeri, ikiye tümleyen ve kayan nokta, aritmetiğin gereklerine göre tasarlanmıştı. Metinde böyle bir doğal zorunluluk yoktur. A harfinin hangi sayıya karşılık geleceği bir tasarım kararıdır ve bu kararın, yazan ile okuyan taraflarca paylaşılması gerekir.

Bu dersin konusu, o paylaşımın nasıl kurulduğudur. İki ayrı soru vardır ve karıştırılmaları yaygın hata kaynağıdır: hangi karakterin hangi numarayı aldığı, ve o numaranın baytlara nasıl yazıldığı.

Karakter Kümesi ve Kodlama

Karakter kümesi (character set), hangi karakterlerin var olduğunu ve her birine hangi numaranın verildiğini söyler. Bu numaraya kod noktası (code point) denir.

Kodlama (encoding), kod noktasının baytlara nasıl yazıldığını söyler.

Küçük kümelerde bu iki katman çakışır: karakter sayısı 256’yı geçmiyorsa her kod noktası tek bir bayta sığar ve ayrım görünmez olur. Küme büyüdüğünde ayrım zorunlu hâle gelir — tek baytın taşıyamayacağı bir numarayı yazmanın birden çok yolu vardır ve hangisinin kullanıldığı belirtilmelidir.

ASCII

ASCII, 7 bitlik bir karakter kümesidir: 27=1282^7 = 128 kod noktası tanımlar. Yerleşim rastgele değildir, aritmetik kolaylıklar gözetilerek düzenlenmiştir:

Aralık (onaltılık) İçerik
001F Denetim karakterleri (satır sonu, sekme, dosya sonu vb.)
20 Boşluk
3039 09 rakamları
415A AZ büyük harfleri
617A az küçük harfleri

Rakamların ardışık olması, bir rakam karakterinden sayısal değerine geçmeyi tek çıkarma işlemine indirger: '7' - '0' = 7. Büyük ve küçük harflerin arasındaki fark tam olarak 0x20’dir, yani tek bir bit: A = 0x41 = 0100 0001, a = 0x61 = 0110 0001. Harf büyütme ve küçültme, ASCII sınırları içinde tek bir bit işlemidir.

Kursun ortak örneği burada üçüncü yorumuna kavuşur. 0x41424344 örüntüsünün dört baytı 0x41, 0x42, 0x43, 0x44’tür; ASCII karşılıkları:

0x41424344"ABCD"\texttt{0x41424344} \longrightarrow \texttt{"ABCD"}

Aynı otuz iki bit, işaretsiz tam sayı olarak 1.094.861.6361.094.861.636, binary32 olarak 12,14142227172851562512{,}141422271728515625, metin olarak ABCD okunur. Bir dosyanın veya ağ paketinin içeriğini doğru okumak, bu üç yorumdan hangisinin geçerli olduğunu bilmeyi gerektirir; biçim tanımının işi budur.

Kod Sayfaları ve Bıraktıkları Sorun

ASCII yalnızca 128 kod noktası tanımladığından, sekiz bitlik baytın üst yarısı boş kalır. Türkçedeki ğ, ş, ı gibi harfler bu boş yarıya yerleştirilmiştir — ancak her dil topluluğu kendi yerleştirmesini yaptığı için birbiriyle uyumsuz kod sayfaları ortaya çıkmıştır.

Sonuç, aynı baytın hangi kod sayfasıyla okunduğuna göre farklı harf vermesidir. Yanlış kod sayfasıyla açılan metinde harflerin bozuk simgelere dönüşmesi bu uyumsuzluğun belirtisidir. Daha ağır bir sınır da vardır: tek bayt en fazla 256 karakter taşır; çok dilli tek bir belge, kod sayfası yaklaşımıyla yazılamaz.

Unicode

Unicode bu sorunu, karakter kümesini kodlamadan ayırarak çözer. Dünyadaki yazı sistemlerinin karakterlerine tek bir numara uzayı içinde kod noktası atar; gösterimi U+ önekiyle ve onaltılık yazılır:

Karakter Kod noktası
A U+0041
ç U+00E7
ğ U+011F
U+6F22

Kod noktası uzayı U+0000 ile U+10FFFF arasındadır; bu, bir milyondan fazla kod noktası demektir. İlk 128 kod noktası ASCII ile birebir aynıdır — bu, geriye dönük uyumluluğu sağlayan bilinçli bir karardır.

Unicode bir kodlama değildir: U+011F numarasının baytlara nasıl yazılacağını söylemez. Bunu kodlamalar söyler.

UTF-8

UTF-8, kod noktasının büyüklüğüne göre 1 ile 4 arasında bayt kullanır. Bayt yapısı, ilk baytın önek bitlerinden okunur:

Kod noktası aralığı Bayt sayısı Örüntü
U+0000 – U+007F 1 0xxxxxxx
U+0080 – U+07FF 2 110xxxxx 10xxxxxx
U+0800 – U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
U+10000 – U+10FFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Kod noktasının bitleri, x ile gösterilen yuvalara soldan sağa yerleştirilir. ç (U+00E7 = 0000 0000 1110 0111) iki bayt gerektirir; anlamlı bitleri 00011 100111 olarak bölünür:

11000011 10100111=0xC3 0xA7\texttt{110}\underline{00011}\ \texttt{10}\underline{100111} = \texttt{0xC3\ 0xA7}

Aynı kuralla (U+6F22) üç bayta yazılır: 0xE6 0xBC 0xA2.

Tasarımın üç sonucu vardır:

  1. ASCII uyumluluğu. U+007F’ye kadar olan kod noktaları tek bayta, ASCII ile birebir aynı değerlere yazılır. Yalnızca ASCII içeren bir belge, UTF-8 belgesi olarak da geçerlidir.
  2. Kendini eşleyebilme. Devam baytları her zaman 10 ile başlar; bir baytın karakter başlangıcı mı yoksa devamı mı olduğu, tek baştaki bitlerden anlaşılır. Bozulmuş bir akışta bir sonraki karakter sınırı bulunabilir.
  3. Değişken uzunluk. Bir karakterin kaç bayt tuttuğu içeriğe bağlıdır; bu, metin işlemede doğrudan sonuçlar doğurur.

UTF-16 ve UTF-32 alternatif kodlamalardır. UTF-32 her kod noktasını sabit dört bayta yazar; erişimi doğrudandır ama yer tüketimi yüksektir. UTF-16 çoğu karakteri iki bayta yazar, kalanları için vekil çift (surrogate pair) kullanır — yani o da değişken uzunlukludur, sabit sanılması yaygın bir yanılgıdır.

Kod Noktası, Bayt ve Grafem

Değişken uzunluklu kodlamada “uzunluk” tek anlamlı bir kavram değildir. Üç ayrı sayım vardır ve üçü de farklı olabilir:

  • Bayt sayısı — dosyada veya ağda kapladığı yer.
  • Kod noktası sayısı — Unicode numaralarının adedi.
  • Grafem sayısı — kullanıcının “harf” olarak gördüğü birimler.

Üçüncü ayrımın kaynağı birleştirici işaretlerdir. é karakteri iki farklı biçimde yazılabilir: tek kod noktası olarak U+00E9, veya e (U+0065) ile birleştirici aksan işaretinin (U+0301) art arda gelmesiyle. İki biçim ekranda aynı görünür, kod noktası sayıları farklıdır ve doğrudan karşılaştırıldıklarında eşit çıkmazlar. Normalleştirme işlemi, karşılaştırma öncesinde iki biçimden birini seçerek bu farkı ortadan kaldırır.

Harf büyütme ve küçültmenin de dilden bağımsız olmadığı burada görülür. ASCII’de tek bit olan işlem, Türkçede iİ ve ıI eşleşmelerini gerektirir; dil bilgisi olmadan yapılan dönüşüm yanlış sonuç verir.

metin = "ABCD"
print(metin.encode("utf-8"))          # b'ABCD'          — 4 bayt

turkce = "çağ"
print(turkce.encode("utf-8"))         # b'\xc3\xa7a\xc4\x9f'
print(len(turkce), len(turkce.encode("utf-8")))   # 3 5

print(ord("ç"), hex(ord("ç")))        # 231 0xe7        — kod noktası U+00E7
print(chr(0x6F22))                    # 漢

ayrisik = "e\u0301"                   # e + birleştirici aksan (U+0301)
tek = "\u00e9"                        # tek kod noktası (U+00E9)

print(ayrisik == tek)                 # False
print(len(ayrisik), len(tek))         # 2 1

import unicodedata
print(unicodedata.normalize("NFC", ayrisik) == tek)    # True

len(turkce) sonucunun 33, kodlanmış hâlinin 55 bayt olması, iki sayımın neden karıştırılmaması gerektiğini gösterir. Sabit uzunluk varsayan bir dizgi işlemi — örneğin “ilk üç baytı al” — çok baytlı bir karakteri ortasından böler ve geçersiz bayt dizisi üretir.

Özet

  • Karakter kümesi karakterlere numara (kod noktası) atar; kodlama o numaranın baytlara nasıl yazılacağını belirler. İki katman ayrıdır.
  • ASCII 128 kod noktası tanımlar; rakamlar ve harfler ardışıktır, büyük–küçük harf farkı tek bittir. Kursun ortak örneği 0x41424344, ASCII yorumunda ABCD metnidir.
  • Tek baytlık kod sayfaları hem uyumsuzdur hem de 256 karakterle sınırlıdır; çok dilli metin bu yaklaşımla yazılamaz.
  • Unicode tek bir kod noktası uzayı tanımlar ve ilk 128 kod noktasında ASCII ile örtüşür.
  • UTF-8 kod noktasını 1–4 bayta yazar; ASCII ile uyumludur, devam baytları 10 ile başladığı için kendini eşleyebilir.
  • Bayt sayısı, kod noktası sayısı ve grafem sayısı farklı büyüklüklerdir; birleştirici işaretler ve dile bağlı harf dönüşümleri bu farkı görünür kılar.

Sonraki Adım

Bir karakterin birden çok bayta yazıldığı anlaşıldı. Peki bu baytlar belleğe hangi sırayla yerleşir? Sayılar için de aynı soru geçerlidir: 0x41424344 değerinin dört baytı bellekte hangi sırada durur? Sonraki ders, bu sıranın donanıma göre değiştiğini ve veri iki sistem arasında taşınırken neden bir sorun kaynağına dönüştüğünü ele alacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat