Çı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 , hane ayraçları ( 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 ayraç 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.
- virgülden önce tek hane
- duyarlık 0 değilse virgül
- virgülden sonra duyarlık adet hane (varsayılan duyarlık 6'dır)
e
karakteri ("10 üzeri" anlamında)- üs sıfırdan küçükse
-
, değilse+
karakteri - en az iki hane olarak üs değeri
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:
0x
karakterleri- tek on altılı hane
- duyarlık 0 değilse virgül
- virgülden sonra duyarlık adet hane, veya duyarlık belirtilmemişse gerektiği kadar hane
p
karakteri ("2 üzeri" anlamında)- üssün değerine göre
-
veya+
karakteri - en az bir hane olarak üs değeri; (0 değerinin üs değeri 0'dı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
türlertrue
veyafalse
olarak- tamsayılar
%d
gibi - kesirli sayılar
%g
gibi - dizgiler UTF-8 kodlamasıyla; duyarlık, en fazla kaç bayt kullanılacağını belirler (UTF-8 kodlamasında karakter sayısıyla bayt sayısının eşit olmayabileceklerini hatırlayın; örneğin "ağ" dizgisi toplam 3 bayt uzunluğunda 2 karakterden oluşur)
- yapı ve sınıf nesneleri, türün
toString()
üye işlevinin ürettiği dizgi olarak; duyarlık, en fazla kaç bayt kullanılacağını belirler - diziler, elemanları yan yana sıralanarak
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
ayraç
Virgül karakteri, sayıların hanelerinin ayraçlarla gruplandırılmalarını sağlar. Varsayılan hane sayısı 3, virgülden sonra belirtilen farklı bir değerle değiştirilebilir:
writefln("%,f", 1234.5678); // Üçer üçer writefln("%,s", 1000000); // Üçer üçer writefln("%,2s", 1000000); // İkişer ikişer
1,234.567,800 1,000,000 1,00,00,00
Hane sayısı *
karakteri olarak belirtildiğinde asıl değer bir sonraki parametre değerinden okunur (o parametre int
olmak zorundadır).
writefln("%,*s", 1, 1000000); // Birer birer
1,0,0,0,0,0,0
Benzer biçimde, ayraç karakteri de virgülden sonra soru işareti kullanılarak belirtilebilir. Kullanılması istenen ayraç karakteri, sayıdan önceki parametre olarak belirtilir:
writefln("%,?s", '.', 1000000); // Ayraç noktadır
1.000.000
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
o
için: sekizli sayının ilk karakteri her zaman için 0 olarak yazdırılırx
veX
için: sayı sıfır değilse, başına0x
veya0X
gelir- kesirli sayılarda: virgülden sonra hane olmasa da virgül yazdırılır
g
veG
için: virgülden sonra sağdaki sıfırlar atılmaz
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:
%(
: Eleman düzeni başı%s
: Her elemanın düzeni%)
: Eleman düzeni sonu
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
- Girilen tamsayıyı on altılı düzende yazdıran bir program yazın.
- 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.