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

düzen: [format], bilginin giriş ve çıkışta nasıl düzenlendiği
iç olanak: [core feature], dilin kütüphane gerektirmeyen bir olanağı
işaretli tür: [signed type], eksi ve artı değer alabilen tür
işaretsiz tür: [unsigned type], yalnızca artı değer alabilen tür
işlev: [function], programdaki bir kaç adımı bir araya getiren program parçası
parametre: [parameter], işleve işini yapması için verilen bilgi
parametre değeri: [argument], işleve parametre olarak verilen bir değer
Phobos: [Phobos], D dilinin standart kütüphanesi
... bütün sözlük



İngilizce Kaynaklar


Diğer




Çıktı Düzeni

Diğer bölümlerden farklı olarak, bu bölüm D dilinin iç olanaklarından birisini değil, çıktı düzeni için kullanılan std.format modülünü anlatmaktadır.

Adı std ile başlayan bütün modüller gibi std.format da D'nin standart kütüphanesi olan Phobos'un bir parçasıdır. Çok büyük bir kütüphane olan Phobos bu kitapta bütünüyle kapsanamamaktadır.

D'nin giriş ve çıkış için kullandığı düzen belirteçlerinin temelde C dilindekiler gibi olduğunu ama bazı farkları bulunduğunu göreceksiniz.

Bir ön hatırlatma olarak bütün düzen dizgisi karakterleri aşağıdaki tablodaki gibidir:

Ayar Karakterleri (birden fazla kullanılabilir)
     -     sola dayalı
     +     işaretli
     #     diğer şekilde
     0     solda 0'lı
  boşluk   solda boşluklu

Düzen Karakterleri
     s     belirteçsiz gibi
     b     ikili
     d     onlu
     o     sekizli
    x,X    on altılı
    f,F    kesirli
    e,E    on üzerili kesirli
    a,A    on altılı kesirli
    g,G    e veya f gibi

     (     eleman düzeni başı
     )     eleman düzeni sonu
     |     eleman ayracı

Şimdiye kadar çıktı için writeln gibi işlevleri gerektiğinde birden fazla parametreyle kullanmıştık. Bu parametreler otomatik olarak karakter eşdeğerlerine dönüştürülerek sırayla çıkışa gönderiliyorlardı.

Bazen bu yeterli değildir. Çıktının belirli bir düzene uyması gerekebilir. Örneğin bir faturanın maddelerini yazdıran şu koda bakalım:

    faturaMaddeleri ~= 1.23;
    faturaMaddeleri ~= 45.6;

    for (int i = 0; i != faturaMaddeleri.length; ++i) {
        writeln("Madde ", i + 1, ": ", faturaMaddeleri[i]);
    }

Çıktısı:

Madde 1: 1.23
Madde 2: 45.6

Oysa faturadaki değerlerin belirli bir düzende, örneğin her zaman için virgülden sonra iki haneyle ve geniş bir alanda sağa dayalı olarak yazılmaları okuma açısından önemli olabilir. (Not: Ben bu bölümde günlük kullanıma uygun olarak "virgül" diyeceğim; ama kesirli sayılarda virgül yerine nokta karakteri kullanılır.):

Madde 1:     1.23
Madde 2:    45.60

İşte çıktı düzeni, böyle konularda yarar sağlar. Şimdiye kadar kullandığımız çıktı işlevlerinin isminde f harfi geçen karşılıkları da vardır: writef() ve writefln(). İsimlerindeki f harfi "düzen, biçim" anlamına gelen "format"ın kısaltmasıdır. Bu işlevlerin ilk parametresi diğer parametrelerin nasıl yazdırılacaklarını belirleyen düzen dizgisidir.

Örneğin, writefln yukarıdaki çıktıyı aşağıdaki gibi bir düzen dizgisi ile üretebilir:

    writefln("Madde %d:%9.02f", i + 1, faturaMaddeleri[i]);

Düzen dizgisi, normal karakterlerden ve özel düzen belirteçlerinden oluşur. Her düzen belirteci % karakteriyle başlar ve bir düzen karakteri ile biter. Yukarıdaki dizgide iki tane düzen belirteci var: %d ve %9.02f.

Her belirteç, düzen dizgisinden sonra verilen parametrelerle sıra ile eşleşir. Örneğin %d ile i + 1, ve %9.02f ile faturaMaddeleri[i]... Her belirteç, eşleştiği parametrenin çıktı düzenini belirler. (Düzen belirteçlerinde parametre numaraları da kullanılabilir. Bunu aşağıda göstereceğim.)

Düzen dizgisi içinde bulunan ve belirteçlere ait olmayan karakterler, oldukları gibi yazdırılırlar. Yukarıdaki dizgi içindeki normal karakterleri işaretli olarak şöyle gösterebiliriz: "Madde %d:%9.02f".

Düzen belirteci, çoğunun belirtilmesi gerekmeyen altı parçadan oluşur. Bu bölümlerden birisi olan numara'yı daha aşağıda göstereceğim. Diğer beş bölüm şunlardır (Not: okumayı kolaylaştırmak için aralarında boşluk kullanıyorum; bu bölümler aslında bitişik olarak yazılırlar):

    %  ayar_karakterleri  genişlik  duyarlık  düzen_karakteri

Baştaki % karakterinin ve sondaki düzen karakterinin yazılması şarttır, diğerleri ise isteğe bağlıdır.

% karakterinin böyle özel bir anlamı olduğu için, çıktıda % karakterinin kendisi yazdırılmak istendiğinde %% şeklinde çift olarak yazılır.

düzen_karakteri

b: Tamsayı, ikili sayı düzeninde yazdırılır.

o: Tamsayı, sekizli sayı düzeninde yazdırılır.

x ve X: Tamsayı, on altılı sayı düzeninde yazdırılır; x için küçük harfler, X için büyük harfler kullanılır.

d: Tamsayı, onlu sistemde yazdırılır; eğer işaretli bir türse ve değeri sıfırdan küçükse, başına eksi işareti gelir; aksi durumda işaretsiz bir tür gibi yazdırılır.

    int değer = 12;

    writefln("İkili    : %b", değer);
    writefln("Sekizli  : %o", değer);
    writefln("On altılı: %x", değer);
    writefln("Ondalık  : %d", değer);
İkili    : 1100
Sekizli  : 14
On altılı: c
Ondalık  : 12

e: Kesirli sayı, aşağıdaki bölümlerden oluşacak şekilde yazdırılır.

E: e ile aynı düzende, ama çıktıda E harfiyle

f ve F: Kesirli sayı, onlu sistemde yazdırılır; virgülden önce en az bir hane bulunur; varsayılan duyarlık 6'dır.

g: Kesirli sayı, eğer üs değeri -5 ile duyarlık arasında olacaksa, f gibi; değilse e gibi yazdırılır. duyarlık virgülden sonrasını değil, belirgin hane sayısını belirtir; virgülden sonra belirgin hane yoksa virgül de yazdırılmaz; virgülden sonra en sağdaki sıfırlar yazdırılmazlar.

G: g ile aynı düzende, ama E veya F kullanılmış gibi yazdırılır

a: Kesirli sayı, on altılı sistemde ve aşağıdaki bölümlerden oluşacak şekilde yazdırılır:

A: a ile aynı düzende, ama çıktıda 0X ve P karakterleriyle

    double değer = 123.456789;

    writefln("e ile: %e", değer);
    writefln("f ile: %f", değer);
    writefln("g ile: %g", değer);
    writefln("a ile: %a", değer);
e ile: 1.234568e+02
f ile: 123.456789
g ile: 123.457
a ile: 0x1.edd3c07ee0b0bp+6

s: Parametrenin değeri; düzen dizgisi kullanılmadığı zamandaki gibi, türüne uygun olan şekilde yazdırılır:

    bool b = true;
    int i = 365;
    double d = 9.87;
    string s = "düzenli";
    auto n = File("deneme_dosyasi", "r");
    int[] dz = [ 2, 4, 6, 8 ];

    writefln("bool  : %s", b);
    writefln("int   : %s", i);
    writefln("double: %s", d);
    writefln("string: %s", s);
    writefln("nesne : %s", n);
    writefln("dizi  : %s", dz);
bool  : true
int   : 365
double: 9.87
string: düzenli
nesne : File(55738FA0)
dizi  : [2, 4, 6, 8]
genişlik

Değer için çıktıda ayrılan alanın genişliğini belirler. Eğer genişlik olarak * kullanılmışsa, genişlik değeri bir sonraki parametrenin değeri olarak alınır. Eğer eksi bir sayıysa, - ayar karakteri kullanılmış gibi çalışır.

    int değer = 100;

    writefln("On karakterlik alanda :%10s", değer);
    writefln("Beş karakterlik alanda:%5s", değer);
On karakterlik alanda :       100
Beş karakterlik alanda:  100
duyarlık

Eğer belirtilmişse, nokta karakterinden sonra yazılır. Kesirli sayı türünden olan değerlerin çıktıda kullanılacak olan duyarlığını belirler. Eğer duyarlık olarak * kullanılmışsa, duyarlık değeri bir sonraki parametrenin değeri olarak alınır (o değer int olmak zorundadır). Duyarlık eksi bir sayıysa gözardı edilir.

    double kesirli = 1234.56789;

    writefln("%.8g", kesirli);
    writefln("%.3g", kesirli);
    writefln("%.8f", kesirli);
    writefln("%.3f", kesirli);
1234.5679
1.23e+03
1234.56789000
1234.568
    auto sayı = 0.123456789;
    writefln("Sayı: %.*g", 4, sayı);
Sayı: 0.1235
ayar_karakterleri

Birden fazla ayar karakteri kullanabilirsiniz.

-: parametre değeri; kendisine ayrılan alanda sola dayalı olarak yazdırılır; bu ayar, 0 ayar karakterini geçersiz kılar

    int değer = 123;

    writefln("normalde sağa dayalı:|%10d|", değer);
    writefln("sola dayalı         :|%-10d|", değer);
normalde sağa dayalı:|       123|
sola dayalı         :|123       |

+: değer artı ise başına + karakteri yazdırılır; bu ayar, boşluk ayar karakterini geçersiz kılar

    writefln("eksi değerde etkili değil: %+d", -50);
    writefln("artı değer, + ile        : %+d", 50);
    writefln("artı değer, + olmadan    : %d", 50);
eksi değerde etkili değil: -50
artı değer, + ile        : +50
artı değer, + olmadan    : 50

#: kullanılan düzen_karakteri'ne bağlı olarak, değeri başka şekilde yazdırır

    writefln("Sekizli sıfırla başlar        : %#o", 1000);
    writefln("On altılının başına 0x gelir  : %#x", 1000);

    writefln("Gerekmese de virgüllü olur    : %#g", 1f);
    writefln("Sağdaki sıfırlar da yazdırılır: %#g", 1.2);
Sekizli sıfırla başlar        : 01750
On altılının başına 0x gelir  : 0x3e8
Gerekmese de virgüllü olur    : 1.00000
Sağdaki sıfırlar da yazdırılır: 1.20000

0: sayılarda (değer nan veya infinity değilse), sol tarafa değer için ayrılan alan dolacak kadar 0 yazdırılır; duyarlık da belirtilmişse bu ayar etkisizdir

    writefln("Sekiz genişlikte: %08d", 42);
Sekiz genişlikte: 00000042

boşluk karakteri: değer artı ise, eksi değerlerle alt alta düzgün dursun diye başına tek bir boşluk karakteri yazdırılır

    writefln("Eksi değerde etkisi yok: % d", -34);
    writefln("Artı değer, boşluklu   : % d", 56);
    writefln("Artı değer, boşluksuz  : %d", 56);
Eksi değerde etkisi yok: -34
Artı değer, boşluklu   :  56
Artı değer, boşluksuz  : 56
Parametre numaraları

Yukarıda düzen dizgisi içindeki düzen belirteçlerinin parametrelerle teker teker ve sırayla eşleştirildiklerini gördük. Aslında düzen belirtecinde parametre numarası da kullanılabilir. Bu, belirtecin hangi parametre ile ilgili olduğunu belirler. Parametreler 1'den başlayarak artan sırada numaralanırlar. Parametre numarası % karakterinden hemen sonra ve $ karakteri ile birlikte yazılır:

    %  numara$  ayar_karakterleri  genişlik  duyarlık  düzen_karakteri

Bunun bir yararı, aynı parametrenin birden fazla yerde yazdırılabilmesidir:

    writefln("%1$d %1$x %1$o %1$b", 42);

Yukarıdaki düzen dizgisi 1 numaralı parametreyi dört düzen belirteci yoluyla onlu, on altılı, sekizli, ve ikili sayı sistemlerinde yazdırmaktadır:

42 2a 52 101010

Parametre numaralarının bir diğer kullanım alanı, aynı parametrelerin farklı düzen dizgileriyle kullanılabilmesi ve bu sayede mesajların farklı konuşma dillerinin yazım kurallarına uydurulabilmesidir. Örneğin belirli bir dersteki öğrenci sayısı Türkçe olarak şöyle bildiriliyor olsun:

    writefln("%s sınıfında %s öğrenci var.", sınıf, adet);
1A sınıfında 20 öğrenci var.

Programın örneğin İngilizce'yi de desteklemesi gerektiğini düşünelim. Bu durumda düzen dizgisinin dile uygun olarak daha önceden seçilmiş olması gerekir. Aşağıdaki yöntem bu iş için üçlü işleçten yararlanıyor:

    auto düzenDizgisi = (dil == "tr"
                         ? "%s sınıfında %s öğrenci var."
                         : "There are %s students in room %s.");

    writefln(düzenDizgisi, sınıf, adet);

Ne yazık ki, parametreler düzen belirteçleriyle birer birer eşleştirildiklerinde sınıf ve adet bilgileri İngilizce mesajda ters sırada çıkarlar. Sınıf bilgisi adet yerinde, adet bilgisi de sınıf yerindedir:

There are 1A students in room 20. ← Yanlış: Adet 1A, sınıf 20!

Bunun önüne geçmek için düzen dizgisinde hangi belirtecin hangi parametreye karşılık geldiği 1$ ve 2$ biçiminde parametre numaralarıyla belirtilebilir:

    auto düzenDizgisi = (dil == "tr"
                         ? "%1$s sınıfında %2$s öğrenci var."
                         : "There are %2$s students in room %1$s.");

    writefln(düzenDizgisi, sınıf, adet);

Artık mesajın hem Türkçesi hem de İngilizcesi düzgündür:

1A sınıfında 20 öğrenci var.
There are 20 students in room 1A.
Eleman düzeni

%( ve %) arasındaki düzen belirteçleri bir topluluktaki (veya aralıktaki) elemanlara teker teker uygulanır:

    auto sayılar = [ 1, 2, 3, 4 ];
    writefln("%(%s%)", sayılar);

Yukarıdaki düzen dizgisi üç parçadan oluşuyor:

Her birisine %s düzeni uygulandığında bütün elemanlar çıktıda art arda belirirler:

1234

Eleman düzeninin başı ile sonu arasındaki normal karakterler her eleman için tekrarlanırlar. Örneğin, {%s}, belirteci her elemanın küme parantezleri arasında ve virgüllerle ayrılarak yazdırılmasını sağlar:

    writefln("%({%s},%)", sayılar);

Ancak, düzen belirtecinin sağındaki normal karakterlerin ayraç oldukları kabul edilir ve onlar normalde yalnızca elemanlar arasına yazdırılırlar. Bu yüzden, yukarıdaki örnekteki }, karakterleri sonuncu elemandan sonra yazdırılmazlar:

{1},{2},{3},{4  ← '}' ve ',' karakterleri son eleman için yazdırılmamış

Sağdaki karakterlerin hangilerinin ayraç oldukları ve hangilerinin sonuncu elemandan sonra da yazdırılmalarının gerektiği %| ile belirtilir. Bu belirtecin solundaki karakterler sonuncu eleman için de yazdırılırlar, sağındaki karakterler ise yazdırılmazlar. Örneğin, aşağıdaki düzen dizgisi } karakterini sonuncu elemandan sonra da yazdırır ama , karakterini yazdırmaz:

    writefln("%({%s}%|,%)", sayılar);
{1},{2},{3},{4}  ← '}' karakteri son eleman için de yazdırılmış

Tek başlarına yazdırılan dizgilerden farklı olarak, eleman olarak yazdırılan dizgiler normalde çift tırnaklar arasında yazdırılırlar:

    auto sebzeler = [ "ıspanak", "kuşkonmaz", "enginar" ];
    writefln("%(%s, %)", sebzeler);
"ıspanak", "kuşkonmaz", "enginar"

Bunun istenmediği durumlarda eleman düzeni %( ile değil, %-( ile başlatılır:

    writefln("%-(%s, %)", sebzeler);
ıspanak, kuşkonmaz, enginar

Aynısı karakterler için de geçerlidir. %( kullanıldığında karakterler tek tırnak içinde yazdırılır:

    writefln("%(%s%)", "merhaba");
'm''e''r''h''a''b''a'

%-( kullanıldığında ise tırnaksız olarak yazdırılır:

    writefln("%-(%s%)", "merhaba");
merhaba

Eşleme tablolarında eleman düzeninde iki belirteç kullanılmalıdır: Birincisi anahtarı, ikincisi de değeri temsil eder. Örneğin, aşağıdaki %s (%s) belirteci önce anahtarın parantezsiz olarak, sonra da değerin parantez içinde yazdırılmasını sağlar:

    auto yazıyla = [ 1 : "bir", 10 : "on", 100 : "yüz" ];
    writefln("%-(%s (%s)%|, %)", yazıyla);

%| belirtecinin sağında belirtilen virgülün son eleman için yazdırılmadığına da dikkat edin:

1 (bir), 100 (yüz), 10 (on)
format

Yukarıda anlatılan bütün olanaklar std.string modülünün format işlevi için de geçerlidir. format aynı writef gibi işler ama oluşturduğu bilgiyi çıkışa yazdırmak yerine bir dizgi olarak döndürür:

import std.stdio;
import std.string;

void main() {
    write("Adınız ne? ");
    auto isim = strip(readln());

    auto sonuç = format("Merhaba %s!", isim);
}

Böylece, oluşturulan dizgi daha sonraki ifadelerde kullanılabilir.

Denetlenen düzen dizgisi

format gibi düzen dizgisi alan bütün işlevlerin (writef, writefln, formattedWrite, readf, formattedRead, vs.) başka bir yazım düzeni daha vardır. Düzen dizgisi bir şablon parametre değeri olarak belirtilebilir ve böylece düzen dizgisinin doğruluğu derleme zamanında denetlenebilir:

import std.stdio;

void main() {
    writefln!"%s %s"(1);       // ← derleme HATASI (fazladan %s)
    writefln!"%s"(1, 2);       // ← derleme HATASI (fazladan 2)
    writefln!"%s %d"(1, 2.5);  // ← derleme HATASI (uyumsuz %d ve 2.5)
}

Yukarıdaki ! karakteri, daha sonraki bir bölümde göreceğimiz şablon parametre değeri işlecidir.

(Not: Her ne kadar bu yazım düzeni daha güvenli olsa da derleme zamanını uzatabilir.)

Problemler
  1. Girilen tamsayıyı on altılı düzende yazdıran bir program yazın.
  2. Girilen kesirli sayıyı bir yüzde değeri olarak ve virgülden sonra 2 haneyle yazdıran bir program yazın. Örneğin 1.2345 girildiğinde ekrana yalnızca %1.23 yazsın.