D Programlama Dili – Programlama dersleri ve D referansı
Ali Çehreli

bayt: [byte], 8 bitlik tür
bit: [bit], 0 ve 1 değerlerini alabilen en temel bilgi birimi
dizgi: [string], "merhaba" gibi bir karakter dizisi
karakter: [character], 'a', '€', '\n', gibi en alt düzey metin parçası
karakter kodlaması: [character encoding], karakter kodlarının ifade edilme yöntemi
kod tablosu: [code page], 127'den büyük karakter değerlerinin bir dünya dili için tanımlanmaları
kontrol karakteri: [control character], yeni satır açan '\n', yatay sekme karakteri '\t', gibi özel karakterler
uç birim: [terminal], bilgisayar sistemlerinin kullanıcıyla etkileşen giriş/çıkış birimi; "konsol", "komut satırı", "cmd penceresi", "DOS ekranı", vs.
... bütün sözlük



İngilizce Kaynaklar


Diğer




Karakterler

Karakterler yazıları oluşturan en alt birimlerdir: harfler, rakamlar, noktalama işaretleri, boşluk karakteri, vs. Önceki bölümdeki dizilere ek olarak bu bölümde de karakterleri tanıyınca iki bölüm sonra anlatılacak olan dizgi kavramını anlamak kolay olacak.

Bilgisayar veri türleri temelde bitlerden oluştuklarından, karakterler de bitlerin birleşimlerinden oluşan tamsayı değerler olarak ifade edilirler. Örneğin, küçük harf 'a'nın tamsayı değeri 97'dir ve '1' rakamının tamsayı değeri 49'dur. Bu değerler tamamen anlaşmalara bağlı olarak atanmışlardır ve kökleri ASCII kod tablosuna dayanır.

Karakterler bazı dillerde geleneksel olarak 256 farklı değer tutabilen char türüyle gösterilirler. Eğer char türünü başka dillerden tanıyorsanız onun her harfi barındıracak kadar büyük bir tür olmadığını biliyorsunuzdur. D'de üç farklı karakter türü bulunur. Buna açıklık getirmek için bu konunun tarihçesini gözden geçirelim.

Tarihçe
ASCII Tablosu

Donanımın çok kısıtlı olduğu günlerde tasarlanan ilk ASCII tablosu 7 bitlik değerlerden oluşuyordu ve bu yüzden ancak 128 karakter değeri barındırabiliyordu. Bu değerler İngiliz alfabesini oluşturan 26 harfin küçük ve büyük olanlarını, rakamları, sık kullanılan noktalama işaretlerini, programların çıktılarını uç birimlerde gösterirken kullanılan kontrol karakterlerini, vs. ifade etmek için yeterliydi.

Örnek olarak, "merhaba" metnindeki karakterlerin ASCII kodları sırasıyla şöyledir (bu gösterimlerde okumayı kolaylaştırmak için bayt değerleri arasında virgül kullanıyorum):

109,101,114,104,97,98,97

Her bir değer bir harfe karşılık gelir. Örneğin, iki 'a' harfi için iki adet 97 değeri kullanılmıştır.

Donanımdaki gelişmeler doğrultusunda ASCII tablosundaki kodlar daha sonra 8 bite çıkartılarak 256 karakter destekleyen Genişletilmiş (Extended) ASCII tablosu tanımlanmıştır.

IBM Kod Tabloları

IBM firması ASCII tablosuna dayanan ve 128 ve daha büyük karakter değerlerini dünya dillerine ayıran bir dizi kod tablosu tanımladı. Bu kod tabloları sayesinde İngiliz alfabesinden başka alfabelerin de desteklenmeleri sağlanmış oldu. Örneğin, Türk alfabesine özgü karakterler IBM'in 857 numaralı kod tablosunda yer aldılar.

Her ne kadar ASCII'den çok daha yararlı olsalar da, kod tablolarının önemli sorunları vardır: Yazının doğru olarak görüntülenebilmesi için yazıldığı zaman hangi kod tablosunun kullanıldığının bilinmesi gerekir çünkü farklı kod tablolarındaki kodlar farklı karakterlere karşılık gelirler. Örneğin, 857 numaralı kod tablosunda 'Ğ' olan karakter 437 numaralı kod tablosu ile görüntülendiğinde 'ª' karakteri olarak belirir. Başka bir sorun, yazı içinde birden fazla dilin karakteri kullanıldığında kod tablolarının yetersiz kalmalarıdır. Ayrıca, 128'den fazla özel karakteri olan diller zaten 8 bitlik bir tabloda ifade edilemezler.

ISO/IEC 8859 Kod Tabloları

Uluslararası standartlaştırma çalışmaları sonucunda ISO/IEC 8859 standart karakter kodları tanımlanmış ve örneğin Türk alfabesinin özel harfleri 8859-9 tablosunda yer almışlardır. Yapısal olarak IBM'in tablolarının eşdeğeri olduklarından IBM'in kod tablolarının sorunları bu standartta da bulunur. Hatta, Felemenkçe'nin ij karakteri gibi bazı karakterler bu tablolarda yer bulamamışlardır.

Unicode

Unicode standardı bu sorunları çözer. Unicode, dünya dillerindeki ve yazı sistemlerindeki harflerin, karakterlerin, ve yazım işaretlerinin yüz binden fazlasını tanımlar ve her birisine farklı bir kod verir. Böylece, Unicode'un tanımladığı kodları kullanan metinler bütün dünya karakterlerini hiçbir karışıklık ve kısıtlama olmadan bir arada bulundurabilirler.

Unicode kodlama çeşitleri

Unicode, her bir karaktere bir kod değeri verir. Örnek olarak, 'Ğ' harfinin Unicode'daki değeri 286'dır. Unicode'un desteklediği karakter sayısı o kadar fazla olunca, karakterleri ifade eden değerler de doğal olarak artık 8 bitle ifade edilemezler. Örneğin, kod değeri 255'ten büyük olduğundan 'Ğ'nin en az 2 baytla gösterilmesi gerekir.

Karakterlerin elektronik ortamda nasıl ifade edildiklerine karakter kodlaması denir. Yukarıda "merhaba" dizgisinin karakterlerinin ASCII kodlarıyla nasıl ifade edildiklerini görmüştük. Şimdi Unicode karakterlerinin standart kodlamalarından üçünü göreceğiz.

UTF-32: Bu kodlama her Unicode karakteri için 32 bit (4 bayt) kullanır. "merhaba"nın UTF-32 kodlaması da ASCII kodlamasıyla aynıdır. Tek fark, her karakter için 4 bayt kullanılmasıdır:

0,0,0,109, 0,0,0,101, 0,0,0,114, 0,0,0,104, 0,0,0,97,
0,0,0,98, 0,0,0,97

Başka bir örnek olarak, ifade edilecek metnin örneğin "aĞ" olduğunu düşünürsek:

0,0,0,97, 0,0,1,30

Not: Baytların sıraları farklı platformlarda farklı olabilir.

'a'da bir ve 'Ğ'de iki adet anlamlı bayt olduğundan toplam beş adet de sıfır bulunmaktadır. Bu sıfırlar her karaktere 4 bayt verebilmek için gereken doldurma baytları olarak düşünülebilir.

Dikkat ederseniz, bu kodlama her zaman için ASCII kodlamasının 4 katı yer tutmaktadır. Metin içindeki karakterlerin büyük bir bölümünün İngiliz alfabesindeki karakterlerden oluştuğu durumlarda, çoğu karakter için 3 tane de 0 kullanılacağından bu kodlama duruma göre fazla savurgan olabilir.

Öte yandan, karakterlerin her birisinin tam olarak 4 bayt yer tutuyor olmasının getirdiği yararlar da vardır. Örneğin, bir sonraki Unicode karakteri hiç hesap gerektirmeden her zaman için tam dört bayt ötededir.

UTF-16: Bu kodlama, Unicode karakterlerinin çoğunu 16 bitle (2 bayt) gösterir. İki bayt yaklaşık olarak 65 bin değer tutabildiğinden, yaklaşık yüz bin Unicode karakterinin geri kalan 35 bin kadarı için daha fazla bayt kullanmak gerekir.

Örnek olarak "aĞ" UTF-16'da aşağıdaki 4 bayt olarak kodlanır:

0,97, 1,30

Not: Baytların sıraları farklı ortamlarda farklı olabilir.

Bu kodlama çoğu belgede UTF-32'den daha az yer tutar ama nadir kullanılan bazı karakterler için ikiden fazla bayt kullandığından işlenmesi daha karmaşıktır.

UTF-8: Bu kodlama, karakterleri en az 1 ve en fazla 4 baytla ifade eder. Eğer karakter ASCII tablosundaki karakterlerden biriyse, tek baytla ve aynen ASCII tablosundaki değeriyle ifade edilir. Bunların dışındaki karakterlerin bazıları 2, bazıları 3, diğerleri de 4 bayt olarak ifade edilirler. Türk alfabesinin İngiliz alfabesinde bulunmayan özel karakterleri 2 baytlık gruptadırlar.

Çoğu belge için UTF-8 bütün kodlamalar arasında en az yer tutan kodlamadır. Başka bir yararı, ASCII tablosundaki kodlara aynen karşılık geldiğinden, ASCII kodlanarak yazılmış ve İngiliz alfabesini kullanan belgeler de otomatik olarak UTF-8 düzenine uyarlar. Bu kodlamada hiç savurganlık yoktur; bütün karakterler gerçekten gereken sayıda baytla ifade edilirler. Örneğin, "aĞ"ın UTF-8 kodlaması aşağıdaki gibidir:

97, 196,158
D'nin karakter türleri

D'de karakterleri ifade etmek için 3 farklı tür vardır. Bunlar yukarıda anlatılan Unicode kodlama yöntemlerine karşılık gelirler. Temel türlerin tanıtıldığı sayfada gösterildikleri gibi:

Tür Açıklama İlk Değeri
char işaretsiz 8 bit UTF-8 karakter değeri 0xFF
wchar işaretsiz 16 bit UTF-16 karakter değeri 0xFFFF
dchar işaretsiz 32 bit UTF-32 karakter değeri 0x0000FFFF

Başka bazı programlama dillerinden farklı olarak, D'de her karakter aynı uzunlukta olmayabilir. Örneğin, 'Ğ' harfi Unicode'da en az 2 baytla gösterilebildiğinden 8 bitlik char türüne sığmaz. Öte yandan, dchar 4 bayttan oluştuğundan her Unicode karakterini tutabilir.

Karakter sabitleri

Karakterleri program içinde tek olarak belirtmek gerektiğinde etraflarına tek tırnak işaretleri koyulur:

    char  a_harfi = 'a';
    wchar büyük_yumuşak_g = 'Ğ';

Karakter sabitleri için çift tırnak kullanılamaz çünkü o zaman iki bölüm sonra göreceğimiz dizgi sabiti anlamına gelir: 'a' karakter değeridir, "a" tek karakterli bir dizgidir.

Türk alfabesindeki bazı harflerin Unicode kodları 2 bayttan oluştuklarından char türündeki değişkenlere atanamazlar.

Karakterleri sabit olarak program içine yazmanın bir çok yolu vardır:

Bu yöntemler karakterleri çift tırnak içinde bir arada yazdığınız durumlarda da geçerlidir. Örneğin, aşağıdaki iki satır aynı çıktıyı verirler:

    writeln("Ağ fiyatı: 10.25€");
    writeln("\x41\u011f fiyatı: 10.25\€");
Kontrol karakterleri

Bazı karakterler yalnızca metin düzeniyle ilgilidirler; kendilerine özgü görünümleri yoktur. Örneğin, uç birime yeni bir satıra geçileceğini bildiren yeni satır karakterinin gösterilecek bir şekli yoktur; yalnızca yeni bir satıra geçilmesini sağlar. Böyle karakterlere kontrol karakteri denir. Kontrol karakterleri \özel_harf söz dizimiyle ifade edilirler.

Yazım İsim Açıklama
\n yeni satır Yeni satıra geçirir
\r satır başı Satırın başına götürür
\t sekme Bir sonraki sekme noktasına kadar boşluk bırakır

Örneğin, çıktıda otomatik olarak yeni satır açmayan write bile \n karakterlerini yeni satır açmak için kullanır. Yazdırılacak metnin içinde istenen noktalara \n karakterleri yerleştirmek o noktalarda yeni satır açılmasını sağlar:

    write("birinci satır\nikinci satır\nüçüncü satır\n");

Çıktısı:

birinci satır
ikinci satır
üçüncü satır
Tek tırnak ve ters bölü

Tek tırnak karakterinin kendisini tek tırnaklar arasında yazamayız çünkü derleyici ikinci tırnağı gördüğünde tırnakları kapattığımızı düşünür: '''. İlk ikisi açma ve kapama tırnakları olarak algılanırlar, üçüncüsü de tek başına algılanır ve yazım hatasına neden olur.

Ters bölü karakteri de başka özel karakterleri ifade etmek için kullanıldığından, derleyici onu bir özel karakterin başlangıcı olarak algılar: '\'. Derleyici \' yazımını bir özel karakter olarak algılar ve baştaki tek tırnakla eşlemek için bir tane daha tek tırnak arar ve bulamaz.

Bu iki karakteri sabit olarak yazmak gerektiğinde başlarına bir ters bölü daha yazılır:

Yazım İsim Açıklama
\' tek tırnak Tek tırnağın karakter olarak tanımlanmasına olanak verir: '\''
\\ ters bölü Ters bölü karakterinin yazılmasına olanak verir: '\\' veya "\\"
std.uni modülü

std.uni modülü Unicode karakterleriyle ilgili yardımcı işlevler içerir. Bu modüldeki işlevleri kendi belgesinde bulabilirsiniz.

is ile başlayan işlevler karakterle ilgili sorular cevaplarlar: cevap yanlışsa false, doğruysa true döndürürler. Bu işlevler mantıksal ifadelerde kullanışlıdırlar:

to ile başlayan işlevler verilen karakteri kullanarak yeni bir karakter üretirler:

Aşağıdaki program bütün bu işlevleri kullanmaktadır:

import std.stdio;
import std.uni;

void main() {
    writeln("ğ küçük müdür? ", isLower('ğ'));
    writeln("Ş küçük müdür? ", isLower('Ş'));

    writeln("İ büyük müdür? ", isUpper('İ'));
    writeln("ç büyük müdür? ", isUpper('ç'));

    writeln("z harf midir? ",       isAlpha('z'));
    writeln("\€ harf midir? ", isAlpha('\€'));

    writeln("'yeni satır' boşluk mudur? ", isWhite('\n'));
    writeln("alt çizgi boşluk mudur? ",    isWhite('_'));

    writeln("Ğ'nin küçüğü: ", toLower('Ğ'));
    writeln("İ'nin küçüğü: ", toLower('İ'));

    writeln("ş'nin büyüğü: ", toUpper('ş'));
    writeln("ı'nın büyüğü: ", toUpper('ı'));
}

Çıktısı:

ğ küçük müdür? true
Ş küçük müdür? false
İ büyük müdür? true
ç büyük müdür? false
z harf midir? true
€ harf midir? false
'yeni satır' boşluk mudur? true
alt çizgi boşluk mudur? false
Ğ'nin küçüğü: ğ
İ'nin küçüğü: i
ş'nin büyüğü: Ş
ı'nın büyüğü: I
Türk alfabesinin şanssız harfleri: ı ve i

'ı' ve 'i' harflerinin küçük ve büyük biçimleri Türk alfabesinde tutarlıdır: noktalıysa noktalı, noktasızsa noktasız. Oysa çoğu yabancı alfabede bu konuda bir tutarsızlık vardır: noktalı 'i'nin büyüğü noktasız 'I'dır.

Bilgisayar sistemlerinin temelleri İngiliz alfabesiyle başladığından 'i'nin büyüğü 'I, 'I'nın küçüğü ise 'i'dir. Bu yüzden bu iki harf için özel dikkat göstermek gerekir:

import std.stdio;
import std.uni;

void main() {
    writeln("i'nin büyüğü: ", toUpper('i'));
    writeln("I'nın küçüğü: ", toLower('I'));
}

İstenmeyen çıktısı:

i'nin büyüğü: I
I'nın küçüğü: i

Karakter kodları kullanılarak yapılan küçük-büyük dönüşümleri ve harf sıralamaları aslında bütün alfabeler için sorunludur.

Örneğin, 'I'nın küçüğünün 'i' olarak dönüştürülmesi Azeri ve Kelt alfabeleri için de yanlıştır.

Benzer sorunlar harflerin sıralanmalarında da bulunur. Örneğin, 'ğ' gibi Türk alfabesine özgü harfler 'z'den sonra sıralandıkları gibi, 'á' gibi aksanlı harfler İngiliz alfabesinde bile 'z'den sonra gelirler.

Girişten karakter okumadaki sorunlar

Unicode karakterleri girişten okunurken beklenmedik sonuçlarla karşılaşılabilir. Bunlar genellikle karakter ile ne kastedildiğinin açık olmamasındandır. Daha ileriye gitmeden önce bu sorunu gösteren bir programa bakalım:

import std.stdio;

void main() {
    char harf;
    write("Lütfen bir harf girin: ");
    readf(" %s", &harf);
    writeln("Okuduğum harf: ", harf);
}

Yukarıdaki programı Unicode kodlaması kullanılmayan bir ortamda çalıştırdığınızda programın girişinden aldığı Türkçe harfleri belki de doğru olarak yazdırdığını görebilirsiniz.

Öte yandan, aynı programı çoğu Linux uç biriminde olduğu gibi bir Unicode ortamında çalıştırdığınızda, yazdırılan harfin sizin yazdığınızla aynı olmadığını görürsünüz. Örneğin, UTF-8 kodlaması kullanan bir uç birimde ASCII tablosunda bulunmayan bir harf girilmiş olsun:

Lütfen bir harf girin: ğ
Okuduğum harf:           ← girilen harf görünmüyor

Bunun nedeni, UTF-8 kodlaması kullanan uç birimin ASCII tablosunda bulunmayan 'ğ' gibi harfleri birden fazla kod ile temsil etmesi, ve readf'in char okurken bu kodlardan yalnızca birincisini alıyor olmasıdır. O char da asıl karakteri temsil etmeye yetmediğinden, writeln'ın yazdırdığı eksik kodlanmış olan harf uç birimde gösterilememektedir.

char olarak okunduğunda harfin kendisinin değil, onu oluşturan kodların okunmakta olduklarını harfi iki farklı char olarak okuyarak görebiliriz:

import std.stdio;

void main() {
    char birinci_kod;
    char ikinci_kod;

    write("Lütfen bir harf girin: ");
    readf(" %s", &birinci_kod);
    readf(" %s", &ikinci_kod);

    writeln("Okuduğum harf: ", birinci_kod, ikinci_kod);
}

Program girişten iki char okumakta ve onları aynı sırada çıkışa yazdırmaktadır. O char değerlerinin art arda uç birime gönderilmiş olmaları, bu sefer harfin UTF-8 kodlamasını standart çıkış tarafında tamamlamakta ve karakter doğru olarak gösterilmektedir:

Lütfen bir harf girin: ğ
Okuduğum harf: ğ

Bu sonuçlar standart giriş ve çıkışın char akımları olmalarından kaynaklanır. Karakterlerin iki bölüm sonra göreceğimiz dizgiler aracılığıyla aslında çok daha rahat okunduklarını göreceksiniz.

D'nin Unicode desteği

Unicode çok büyük ve karmaşık bir standarttır. D, Unicode'un oldukça kullanışlı bir alt kümesini destekler.

Unicode ile kodlanmış olan bir metin en aşağıdan en yukarıya doğru şu düzeylerden oluşur:

Özet