COPYFILE yetmediğinde: satır satır dosya işlemleri

Bir önceki yazıda COPYFILE ile dosyaları olduğu gibi taşımaktan bahsetmiştim. Ama bazen dosyanın içeriğiyle ilgilenmeniz gerekir — bir CSV'yi satır satır okuyup her satırdan bir kayıt üretmek, ya da bir log dosyasına sürekli yeni satırlar eklemek gibi. Bunun için TROIA'nın dosya G/Ç komut ailesi var: OPEN FILE, PUT, GET, GETBLOCK, CLOSE FILE.

Dosya açma ve kapama

OPEN FILE {dosyayolu} [FORNEW|FORREAD|FORAPPEND] [FILEID {fileid}];
CLOSE FILE [FILEID {fileid}];

Üç mod farklı davranır:

  • FORNEW: Yeni dosya oluşturur, mevcut istemci dosyasını kopyalamaz.
  • FORREAD: İstemci dosyasını önce sunucuya kopyalar, sonra okuma için açar.
  • FORAPPEND: Varsayılan mod; istemci dosyasını sunucuya kopyalar.

Her iki komut da başarısızlıkta SYS_STATUS'u 1 yapar ve SYS_STATUSERROR'a hata detayını yazar — bu yüzden OPEN FILE'dan hemen sonra SYS_STATUS kontrolü yapılmadan PUT/GET çağırmak, "dosya açılamadı ama program kodu bunu fark etmeden devam etti" tarzı sinsi hataların klasik kaynağıdır.

İstemci tarafındaki bir yola işaret ederken yol * öneki alır: '*C:\TMP\dosya.txt'.

Yazma: PUT

PUT {degerlistesi} [CODEPAGE {kodlama}] [NOENDOFLINE] [FILEID {fileid}];

PUT varsayılan olarak her çağrıdan sonra satır sonu (\n\r) ekler; bunu istemiyorsanız NOENDOFLINE kullanılır. Kodlama belirtilmezse varsayılan ISO8859_9 (Türkçe Latin-5) kullanılır — bu detay, bir dosyayı UTF-8 bekleyen harici bir sisteme gönderirken "Türkçe karakterler bozuk geliyor" şikayetinin neredeyse her zaman gerçek sebebidir; CODEPAGE parametresini açıkça belirtmek bu sınıf hataları baştan önler.

Okuma: GET ve GETBLOCK

GET {degiskenlistesi} [CODEPAGE {kodlama}] [FILEID {fileid}];
GETBLOCK {degisken}, {ayirici} [CODEPAGE {kodlama}] [FILEID {fileid}];

GET tek satır okur, dosya sonunda boş string döner — yani döngü koşulu genelde "boş string dönene kadar oku" şeklinde kurulur. GETBLOCK ise belirtilen ayırıcıya kadar okur; boş ayırıcı verilirse dosyanın tamamını tek seferde okur (küçük dosyalar için pratik, büyük dosyalarda bellek tüketimine dikkat).

Gerçek bir örnek: CSV'yi satır satır okuyup tabloya aktarmak

/* Basit bir CSV dosyasını satır satır okuyup tabloya aktarır. */
LOCAL:
    STRING SATIR;
    TABLE KAYITLAR(KOD CHAR(20), ADI CHAR(100));

OPEN FILE '*C:\TMP\urunler.csv' FORREAD FILEID 1;

IF SYS_STATUS = 1 THEN
    MESSAGE 'Dosya açılamadı: ' + SYS_STATUSERROR TYPE ERROR;
    RETURN;
ENDIF;

GET SATIR FILEID 1;
WHILE SATIR <> '';
    PARSE SATIR ';' KAYITLAR.KOD, KAYITLAR.ADI;
    ADDROW TO KAYITLAR;
    GET SATIR FILEID 1;
ENDWHILE;

CLOSE FILE FILEID 1;

Sık yapılan hatalar

  • FILEID kullanmadan aynı anda birden fazla dosya açmaya çalışmak — FILEID verilmezse sistem varsayılan bir dosya tanımlayıcı kullanır, bu da eşzamanlı erişimi engeller. Birden fazla dosyayla aynı anda çalışıyorsanız her birine ayrı FILEID vermek zorunludur.
  • Döngü sonunda CLOSE FILE çağırmayı unutmak — özellikle bir hata/RETURN ile döngüden erken çıkıldığında dosya açık kalabilir; sonraki çağrılarda "dosya kilitli" hatasıyla karşılaşmamak için CLOSE FILE'ı bir hata durumunda da mutlaka çalışacak şekilde kurgulamak gerekir.
  • GET'in dosya sonunda boş string döndürdüğünü unutup sonsuz döngüye girmek — eğer okunan satır gerçekten boşsa (dosyada boş satır varsa) döngü yanlışlıkla erken sonlanabilir; kritik veri dosyalarında SYS_STATUS'u da kontrol ederek dosya sonunu daha güvenilir tespit etmek daha sağlam bir yaklaşımdır.
  • Kodlama belirtmeden Türkçe karakter içeren dosyalarla çalışmak — yukarıda değindiğim gibi, CODEPAGE parametresi atlanırsa dış sistemle veri alışverişinde karakter bozulmaları en sık karşılaşılan destek talebi haline gelir.

Sıradaki adım

DIGESTFILE ile dosya bütünlüğü kontrolü (checksum/hash) ve ZIPFILE/UNZIPFILE ile bu satır satır okuma-yazma yaklaşımını birlikte kullanan bir örneğe ileride başka bir yazıda değinebilirim; şimdilik dosya G/Ç ailesinin temel taşlarını kapsamış olduk.