Bir programın hesapladığı her şey, yazıya dökülmedikçe program çıktığında yok olur. C'nin dosya arayüzü <stdio.h> içinde yaşar - printf ile aynı başlık - ve bilinçli olarak benzerdir: bir dosya bayt akışıdır ve ekranda çalışan fonksiyonların, akış argümanı alan ikizleri vardır.
Tutamaç tipi FILE *'dır. Bir FILE'ın içine asla bakmazsınız; opak bir tiptir (bkz. typedef) ve yalnızca ona bir gösterici tutarsınız.
Üç Adım
Her dosya işlemi aynı biçimi izler: aç, kullan, kapat.
NULL denetimi isteğe bağlı bir paranoya değildir. fopen; dosya yoksa (okuma kipinde), dizin yazılabilir değilse, yol yanlışsa ya da süreç dosya tutamacı tüketmişse başarısız olur ve bunların her biri NULL döndürür. NULL bir tutamacı kullanmak bir segmentation fault'tur.
Hatayı stdout yerine stderr'e göndermek gelenektir: tamponsuzdur ve gerçek çıktıdan ayrı yönlendirilebilir.
fopen Kipleri
İkinci argüman kısa bir kip metnidir. Onu yanlış yapmak bu alandaki en yıkıcı hatadır, çünkü "w" var olan bir dosyayı sessizce boşaltır.
| Kip | Oku | Yaz | Dosya varsa | Yoksa |
|---|---|---|---|---|
"r" | evet | hayır | başta açılır | başarısız, NULL döner |
"w" | hayır | evet | boşaltılır | oluşturulur |
"a" | hayır | evet | yalnızca sona yazar | oluşturulur |
"r+" | evet | evet | başta açılır | başarısız, NULL döner |
"w+" | evet | evet | boşaltılır | oluşturulur |
"a+" | evet | evet | her yerden okur, sona yazar | oluşturulur |
Bunların herhangi birine b ekleyin ("rb", "wb", "ab+") ve ikili kip elde edin - bu konuda daha fazlası sonda.
Gerçek veri kaybını önleyen iki kural:
- Okumayı kastediyorsanız
"r"kullanın. Yanlış yazılmış bir dosya adında"r+"güvenle başarısız olur;"w+"boş bir dosya oluşturur ve zamandan başka bir şey kaybetmezsiniz. Doğru dosya adında"r"kastedip"w"kullanmak veriyi yok eder. - Günlükler için
"a"kullanın. Akış nereye konumlanmış olursa olsun herfprintfsona düşer ki bir günlüğün istediği tam olarak budur.
Tek Programda Yazmak ve Okumak
Aşağıdaki editör eksiksiz bir gidiş-dönüş çalıştırır - bir dosya oluşturur, içine kayıtlar yazar, kapatır, okumak için yeniden açar ve bulduklarını yazdırır.
fprintf ve fscanf, ilk argümanı bir akış olan printf ve scanf'tir; biçim belirteçleriyle ilgili her şey, name'in taşmasını önleyen %31s genişliği dahil tıpatıp aynıdır.
Döngü koşulu == 2'dir, yani biçimin istediği öğe sayısı. Bunun yerine EOF ile test etmek klasik bir hatadır: bozuk bir satır fscanf'in EOF değil 0 döndürmesine yol açar ve döngü, tüketemediği girdi üzerinde sonsuza dek döner.
fgets ile Satır Satır Okumak
fscanf, katı biçimlendirilmiş veri için elverişlidir. Metin dosyaları için - yapılandırma, günlük, CSV, bir insanın yazdığı her şey - tüm satırları okuyun. Bu döngü klasik olanıdır:
fgets'i doğru varsayılan kılan şeyler:
- Tampon boyutunu alır, dolayısıyla taşamaz.
sizeof linegeçirin, diziyi yeniden boyutlandırdığınızda çağrı doğru kalsın. - Dosya sonunda ya da hatada
NULLdöndürür ki bu temiz bir döngü koşuludur. - Satır tampona sığdığında satır sonunu korur. Bu yararlıdır - aldığınızda
'\n'yoksa satır tamponunuzdan uzundu ve gerisi hâlâ bekliyordur.strcspn(line, "\n")satır sonunun indeksini (ya da yoksa metnin uzunluğunu) bulur, dolayısıyla oraya'\0'atamak her iki durumda da kırpar.
Gerçek bir dosya sonunu bir hatadan ayırmak için döngüden sonra sorun:
if (ferror(in)) {
fprintf(stderr, "okuma hatasi\n");
} else if (feof(in)) {
/* normal son */
}
Bir döngü koşulu olarak while (!feof(fp)) yazmayın. feof yalnızca bir okuma çoktan başarısız olduktan sonra doğru olur, dolayısıyla o döngü son tampon içeriğini fazladan bir kez işler. Bunun yerine okuma fonksiyonunun dönüş değerini test edin - yukarıdaki iki döngünün yaptığı gibi.
Karakter Karakter: fgetc ve fputc
Bayt düzeyindeki işler için - karakter saymak, bir dosyayı dönüştürmek, kopyalamak - fgetc ve fputc çağrı başına bir karakter işler.
Önemli bir ayrıntı: c, char değil int olarak bildirilmiştir. fgetc, olası her bayt değerini ve ayrı nöbetçi EOF'u (-1) döndürebilmek için bir int döndürür. Onu bir char'da saklamak, platformunuzda char'ın işaretli olup olmamasına göre EOF karşılaştırmasını ya hep yanlış ya da 0xFF baytı için yanlışlıkla doğru yapar. Bu, C'nin en eski tuzaklarından biridir.
Hataları Düzgün Denetlemek
Üretim düzeyinde bir okuma şöyle görünür:
errno, son başarısızlığı tarif eden bir kod tutar ve strerror onu bir cümleye çevirir; perror sizin mesajınızı artı o cümleyi tek çağrıda yazdırır. İkisi sırasıyla <errno.h> ve <string.h> ister.
fclose da başarısız olabilir - tamponlanmış veriyi boşaltır ve yazma diske sığmayabilir - dolayısıyla önemli bir şey için kontrol edin:
if (fclose(fp) != 0) {
fprintf(stderr, "bosaltma ve kapatma basarisiz\n");
}
Tek Paragrafta İkili Kip
Metin kipi satır sonlarını çevirebilir (Windows'ta yazarken \n, \r\n olur ve okurken geri döner) ve bazı baytlara özel davranabilir. Metin olmayan veriler için - bir görüntü, olduğu gibi dökülmüş bir yapı, sıkıştırılmış bir blok - b ile açın ve ham bayt taşıyan fread/fwrite kullanın:
fwrite(ptr, size, count, fp), size baytlık count öğe yazar ve kaç tanesini başardığını döndürür; fread onu yansıtır. Bu şekilde yazılan bir dosyanın onu yazan makineye bağlı olduğunun farkında olun - yapı dolgusu, tam sayı boyutu ve bayt sırası hepsi baytlara sızar - dolayısıyla bir önbellek ya da geçici dosya için uygundur, başka programların okuması gereken bir biçim için yanlıştır.
Sık Yapılan Hatalar
fopen'ıNULLiçin kontrol etmemek. Ardından gelen çökme, açmaya değil okumaya yıkılır."r"kastederken"w"ile açmak. Siz fark etmeden dosya boşaltılır.fclose'u unutmak. Tamponlanmış çıktı kaybolur ve kapatılmayan her dosya bir tutamaç sızdırır.while (!feof(fp)). Son satırı iki kez işler. Bunun yerine okuma çağrısını test edin.char c = fgetc(fp).EOFkarşılaştırmasını bozar.intkullanın.- Genişliksiz
fscanf("%s", buf). Klavyede scanf ile olan tampon taşmasının aynısı. - Göreli yollar.
fopen("data.txt", "r"), çalıştırılabilir dosyanın yanına değil çalışma dizinine bakar. Bir dosya "kayboluyorsa" sebebi genellikle budur.
Sıkça Sorulan Sorular
C'de bir dosya nasıl açılır?
FILE *fp = fopen("data.txt", "r"); bir dosyayı okumak için açar ve bir FILE * tutamacı döndürür; başarısız olursa NULL döndürür. Tutamacı kullanmadan önce her zaman NULL kontrolü yapın ve işiniz bittiğinde fclose(fp) çağırın.
C'de fopen kipleri nelerdir?
"r" okuma (dosya var olmalı), "w" yazma (oluşturur ya da var olan bir dosyayı boşaltır), "a" ekleme (oluşturur, sona yazar). + eklemek her birini okuma-yazma yapar: "r+", "w+", "a+". b eklemek ("rb", "wb") ikili kipte açar.
C'de bir dosya satır satır nasıl okunur?
Bir while döngüsünde fgets kullanın: while (fgets(line, sizeof line, fp) != NULL) { ... }. Her satır sonunda ya da tampon dolduğunda durur, dosya sonunda NULL döndürür ve tampon boyutunu geçirdiğiniz için taşamaz.
C'de yazdıktan sonra dosyam neden boş?
Çoğu zaman fclose'u unutmuşsunuzdur. Çıktı tamponlanır, dolayısıyla program anormal biçimde bittiğinde veri hâlâ bellekte duruyor olabilir. fclose boşaltır ve kapatır; fflush(fp) kapatmadan boşaltır. Diğer sebep, az önce yazdığınız dosyayı boşaltan ikinci bir "w" ile açmadır.