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

akım: [stream], nesnelerin art arda erişildiği giriş çıkış birimi
BOM: [BOM, byte order mark], dosyanın en başına yazılan Unicode kodlama belirteci
değişmez: [immutable], programın çalışması süresince kesinlikle değişmeyen
kapsam: [scope], küme parantezleriyle belirlenen bir alan
klasör: [directory], dosyaları barındıran dosya sistemi yapısı, "dizin"
kurma: [construct], yapı veya sınıf nesnesini kullanılabilir duruma getirmek
nesne: [object], belirli bir sınıf veya yapı türünden olan değişken
standart çıkış: [standard output], program çıktısının normalde gönderildiği akım
standart giriş: [standard input], program girişinin normalde okunduğu akım
uç birim: [terminal], bilgisayar sistemlerinin kullanıcıyla etkileşen giriş/çıkış birimi; "konsol", "komut satırı", "cmd penceresi", "DOS ekranı", vs.
üye işlev: [member function], yapı veya sınıfın kendi tanımladığı işlemleri
yapı: [struct], başka verileri bir araya getiren veri yapısı
... bütün sözlük



İngilizce Kaynaklar


Diğer




Dosyalar

Ne kadar güçlü olsalar da, önceki bölümde uç birimlerde kullanıldıklarını gördüğümüz >, <, ve | karakterleri her duruma uygun değildir. Çünkü her program işini yalnızca standart giriş ve çıkışla etkileşerek yapamaz.

Örneğin öğrenci kayıtları ile ilgilenen bir program, standart çıkışını kullanıcıya bir komut menüsü göstermek için kullanıyor olabilir. Standart girişini de kullanıcıdan komut almak için kullandığını düşünürsek, böyle bir programın kayıtlarını tuttuğu öğrenci bilgilerini yazmak için en az bir dosyaya ihtiyacı olacaktır.

Bu bölümde dosya sisteminin klasörlerde barındırdığı dosyalara yazmayı ve dosyalardan okumayı öğreneceğiz.

Temel kavramlar

Dosya işlemleri için std.stdio modülünde tanımlanmış olan File yapısı kullanılır. Henüz yapıları göstermediğim için File nesnelerinin kurulma söz diziminin ayrıntısına girmeyeceğim ve şimdilik bir kalıp olarak kabul etmenizi bekleyeceğim.

Kullanımlarına geçmeden önce dosyalarla ilgili temel kavramların açıklanması gerekir.

Karşı taraf

Bu bölümdeki bilgilerle oluşturulan dosyaların başka ortamlarda hemen okunabileceklerini düşünmeyin. Dosyayı oluşturan taraf ile dosyayı kullanan tarafın en azından dosya düzeni konusundan anlaşmış olmaları gerekir. Örneğin öğrenci numarasının ve isminin dosyaya yazıldıkları sırada okunmaları gerekir.

Bir dosya oluşturup içine bilgiler yazmak, o dosyanın başka bir ortamda açılıp okunması için yeterli olmayabilir. Dosyayı yazan tarafla okuyan tarafın belirli konularda anlaşmış olmaları gerekir. Örneğin dosyaya char[] olarak yazılmış olan bir bilginin wchar[] olarak okunması yanlış sonuç doğurur.

Ek olarak, aşağıdaki kodlar dosyaların başına BOM belirtecini yazmazlar. Bu, dosyalarınızın BOM belirteci gerektiren ortamlarda doğru olarak açılamamasına neden olabilir. ("Byte order mark"ın kısası olan BOM, karakterleri oluşturan UTF kod birimlerinin dosyaya hangi sırada yazılmış olduklarını belirtir.)

Dosya erişim hakları

Dosya sistemi dosyaları programlara çeşitli erişim haklarıyla sunar. Erişim hakları hem performans hem de dosya sağlığı açısından önemlidir.

Konu dosyadan okumak olunca; aynı dosyadan okumak isteyen birden fazla programa aynı anda okuma izni verilmesi, programlar birbirlerini beklemeyecekleri için hız kazancı sağlar. Öte yandan, konu dosyaya yazmak olunca; dosyanın içeriğinin tutarlılığı açısından dosyaya belirli bir anda ancak tek bir programın yazmasına izin verilmelidir; yoksa iki programın birbirlerinden habersiz olarak yazmaları sonucunda dosyanın içeriği tutarsız hale gelebilir.

Dosya açmak

Programın standart giriş ve çıkış akımları olan stdin ve stdout, program başladığında zaten açılmış ve kullanıma hazır olarak gelirler; onları kullanmaya başlamadan önce özel bir işlem gerekmez.

Dosyaların ise diskteki isimleri ve istenen erişim hakları bildirilerek program tarafından açılmaları gerekir. Aşağıdaki örneklerde de göreceğimiz gibi, oluşturulan bir File nesnesi, belirtilen isimdeki dosyanın açılması için yeterlidir:

    File dosya = File("ogrenci_bilgisi", "r");
Dosya kapatmak

Açılan dosyaların mutlaka kapatılmaları da gerekir. Ancak, File nesneleri kendileri sonlanırken erişim sağlamakta oldukları asıl dosyaları da kapattıkları için, normalde bu işin programda açıkça yapılması gerekmez. Dosya, File nesnesinin içinde bulunduğu kapsamdan çıkılırken kendiliğinden kapatılır:

if (bir_koşul) {

    // File nesnesi burada oluşturulmuş ve kullanılmış olsun
    // ...

} // ← Dosya bu kapsamdan çıkılırken otomatik olarak
  //   kapatılır. Açıkça kapatmaya gerek yoktur.

Bazen aynı File nesnesinin başka dosyayı veya aynı dosyayı farklı erişim haklarıyla kullanması istenir. Böyle durumlarda dosyanın kapatılıp tekrar açılması gerekir:

    dosya.close();                       // dosyayı kapatır
    dosya.open("ogrenci_bilgisi", "r");  // dosyayı açar
Dosyaya yazmak ve dosyadan okumak

Dosyalar da karakter akımları olduklarından, writeln ve readf gibi işlevler onlarla da kullanılabilir. Farklı olan, dosya değişkeninin isminin ve bir noktanın da yazılmasının gerekmesidir:

    writeln("merhaba");        // standart çıkışa yazar
    stdout.writeln("merhaba"); // yukarıdakinin uzun yazımıdır
    dosya.writeln("merhaba");  // dosyaya yazar
Dosya sonu için eof()

Bir dosyadan okurken dosyanın sonuna gelinip gelinmediği, "dosya sonu" anlamına gelen "end of file"ın kısaltması olan eof() üye işleviyle denetlenir. Bu işlev dosya sonuna gelindiğinde true döndürür.

Örneğin, aşağıdaki döngü dosyanın sonuna gelene kadar devam eder:

    while (!dosya.eof()) {
        // ...
    }
Klasör işlemleri için std.file modülü

Klasör işlemleri ile ilgili olan std.file modülünün belgesinde işinize yarayacak işlevler bulabilirsiniz. Örneğin, exists belirtilen isimdeki dosyanın mevcut olup olmadığını bildirir:

    if (exists(dosya_ismi)) {
        // dosya mevcut

    } else {
        // dosya mevcut değil
    }
std.stdio.File yapısı

File, C dilindeki standart fopen işlevinin kullandığı erişim belirteçlerini kullanır:

 Belirteç  Anlamı
rokuma erişimi
dosya başından okunacak şekilde hazırlanır
r+okuma ve yazma erişimi
dosya başından okunacak ve başına yazılacak şekilde hazırlanır
wyazma erişimi
dosya yoksa: boş olarak oluşturulur
dosya zaten varsa: içi boşaltılır
w+okuma ve yazma erişimi
dosya yoksa: boş olarak oluşturulur
dosya zaten varsa: içi boşaltılır
asonuna yazma erişimi
dosya yoksa: boş olarak oluşturulur
dosya zaten varsa: içeriği korunur ve sonuna yazılacak şekilde hazırlanır
a+okuma ve sonuna yazma erişimi
dosya yoksa: boş olarak oluşturulur
dosya zaten varsa: içeriği korunur; başından okunacak ve sonuna yazılacak şekilde hazırlanır

Yukarıdaki erişim haklarının sonuna 'b' karakteri de gelebilir ("rb" gibi). O karakter binary mode açma durumunu destekleyen platformlarda etkili olabilir ama POSIX ortamlarında gözardı edilir.

Dosyaya yazmak

Dosyanın önce yazma erişimi ile açılmış olması gerekir:

import std.stdio;

void main() {
    File dosya = File("ogrenci_bilgisi", "w");

    dosya.writeln("İsim  : ", "Zafer");
    dosya.writeln("Numara: ", 123);
    dosya.writeln("Sınıf : ", "1A");
}

Dizgiler bölümünden hatırlayacağınız gibi, "ogrenci_bilgisi" gibi bir dizginin türü string'dir ve değişmezdir. Yani File, dosya ismini ve erişim hakkını string türü olarak kabul eder. Bu yüzden, yine o bölümden hatırlayacağınız gibi, File örneğin char[] türünde bir dizgi ile kurulamaz; kurmak gerektiğinde o dizginin .idup niteliğinin çağrılması gerekir.

Yukarıdaki program, çalıştırıldığı klasör içinde ismi ogrenci_bilgisi olan bir dosya oluşturur veya var olan dosyanın üzerine yazar.

Not: Dosya ismi olarak dosya sisteminin izin verdiği her karakteri kullanabilirsiniz. Ben bu kitapta dosya isimlerinde yalnızca ASCII harfler kullanacağım.

Dosyadan okumak

Dosyanın önce okuma erişimi ile açılmış olması gerekir:

import std.stdio;
import std.string;

void main() {
    File dosya = File("ogrenci_bilgisi", "r");

    while (!dosya.eof()) {
        string satır = strip(dosya.readln());
        writeln("Okuduğum satır -> |", satır);
    }
}

Yukarıdaki program, çalıştırıldığı klasör içindeki ogrenci_bilgisi isimli dosyanın içindeki satırları başından sonuna kadar okur ve standart çıkışa yazdırır.

Problem

Yazacağınız program kullanıcıdan bir dosya ismi alsın, o dosyanın içindeki boş olmayan bütün satırları, dosyanın ismine .bak eklenmiş başka bir dosyaya yazsın. Örneğin, verilen dosyanın ismi deneme.txt ise, boş olmayan satırlarını deneme.txt.bak dosyasına yazsın.