---
title: 'Dosya Biçimleri'
source: 'https://academia.sh/tr/kurslar/veri-hazirlama/dosya-bicimleri'
course: 'Veri Toplama ve Hazırlama'
language: tr
updated: '2026-08-17T18:08:45+00:00'
license: 'CC BY-SA 4.0'
---

# Dosya Biçimleri

Önceki kursun 2.037 satırlık sayfasını üreten dışa aktarım dosyası iki ayrıştırma kararıyla okunuyor ve iki farklı küme çıkıyor: tırnak içini koruyan okuma 2.037 kaydın hepsini çıkarır, naif bölme 2.076 fiziksel satırdan 1.934 kayıt çıkarır ve 142 satırı hiçbir uyarı vermeden düşürür — 78'i adresindeki satır sonundan bölünmüş yarım satır, 64'ü alanı kayan satır. Kalan 1.934 kaydın 33'ü hayatta kalır ama okuma alanında adresten kaymış bir kapı numarası taşır; toplam 984,2 m³ ayrışır ve 1.157 kaydın bölge adı, dosya yanlış kodlamayla çözüldüğü için listeyle eşleşmez. Aynı 2.037 kayıt sütunlu yapıda tutulduğunda toplam okumayı öğrenmek 108.219 bayt yerine 4.074 bayt okumak demektir.

Önceki kurs on bir derste otuz iki karar aldı ve yalnız yedisi sayfadan geri okunabildi. Ama o
sayfanın kendisi bir yerden geldi: 2.037 satır, 1.400 abone, 42.645,6 m³. Bu ders o sayfanın
öncesine bakar ve tek bir soruyu sorar — dosya çözümleme ortamına girerken kaç satır kaybetti.

Cevap kararlara bağlıdır ve kararlar hata vermez. Bir ayrıştırıcı bozuk bir satırla karşılaştığında
durup uyarmaz; o satırı sessizce atlar ve okumaya devam eder. Dersin ölçü birimi budur ve kurs
boyunca sürer: her adımda **giren satır**, **çıkan satır** ve **nereye gitti**. Üçüncüsünün en
önemli kalemi **sessiz kayıp**, yani hiçbir uyarı üretmeden düşen satırdır.

- **VT1.** Dosya kurgudur. Gerçek bir dışa aktarım yerine aynı yapıyı üreten bir üreteç yazıldı;
  tohum görünürdür ve çıktı koşumdan koşuma aynıdır. **Modeldir.**
- **VT2.** Üretilen küme önceki kursun sayfasını verir: 2.037 kayıt, 1.400 farklı abone, 1.877
  farklı abone-dönem çifti, 42.645,6 m³ toplam.
- **VT3.** Ayraç noktalı virgül, tırnak çift tırnaktır; adres alanı her kayıtta tırnak içinde
  yazılır ve dosya UTF-8 yazılır.
- **VT4.** İki toplamın aynı sayılması için eşik 0,05 m³'tür, çünkü okumalar bir ondalıkla
  raporlanıyor ve altındaki bir fark yalnızca yuvarlamadan gelebilir.
- **VT5.** Üretecin verebileceği en büyük okuma 37,0 m³'tür; okuma alanında bundan büyük bir değer
  o alanın okuma taşımadığını gösterir.

## Dosyanın İçinde Ne Var

```js
// kaynak.mjs — bolgesel olcum agindan gelen kurgu bir disa aktarim. MODELDIR:
// gercek bir dosya yerine ayni yapiyi ureten bir uretec yazildi; tohum gorunur
// ve cikti kosumdan kosuma aynidir. Kayitlar M26/K02'nin sayfasini uretir:
// 1400 abone, 1877 abone-donem cifti, 160 yinelenen satir, toplam 2037 kayit.
export const TOHUM = 1246, AYRAC = ";", TIRNAK = '"';
export const BOLGE = ["kuzey", "doğu", "merkez", "güney", "batı"];
export const BASLIK = ["abone_no", "sayac_no", "donem", "adres", "okuma_m3", "bolge"];
const SOKAK = ["Cinar Sok", "Yeni Mah Bahce Cad", "Istasyon Cad", "Dere Sok", "Zeytin Sok"];

export const uretec = (t) => { let s = t >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; }; };

// Kayitlar: once 1400 abonenin ilk donemi, sonra 477 abonenin ikinci donemi,
// sonra ilk 160 satirin yinelenmesi (32'si birebir kopya, 128'i duzeltilmis okuma).
export function kayitlar() {
  const r = uretec(TOHUM), oku = () => 5 + Math.floor(r() * 321) / 10, k = [];
  const yap = (i, donem, okuma) => ({ abone: 10000 + i, sayac: `S-${4000 + i}`,
    donem, adres: `${SOKAK[i % 5]} No ${12 + (i % 88)}`, okuma, bolge: BOLGE[i % 5] });
  for (let i = 0; i < 1400; i++) k.push(yap(i, "2024-01", oku()));
  for (let i = 0; i < 477; i++) k.push(yap(i, "2024-02", oku()));
  for (let i = 0; i < 160; i++) k.push(yap(i, "2024-01", i < 32 ? k[i].okuma : oku()));
  return k;
}

// Kirlilik kurali: her 21. kaydin adresi ayrac icerir (ucte biri ayractan sonra
// ciplak bir kapi numarasiyla biter), geri kalanlardan 50'de biri satir sonu
// icerir. Adres alani her kayitta tirnak icinde yazilir.
export const AYRACLI = (i) => i % 21 === 0;
export const SATIRLI = (i) => !AYRACLI(i) && i % 50 === 13;
export const SAYIYLA = (i) => AYRACLI(i) && (i / 21) % 3 === 0;

export function dosya() {
  const k = kayitlar(), s = [BASLIK.join(AYRAC)];
  k.forEach((x, i) => {
    let adres = x.adres;
    if (AYRACLI(i)) adres += SAYIYLA(i) ? `${AYRAC} ${45 + (i % 30)}` : `${AYRAC} Daire ${1 + i % 8}`;
    else if (SATIRLI(i)) adres += `\nKat ${1 + (i % 6)}`;
    s.push([x.abone, x.sayac, x.donem, TIRNAK + adres + TIRNAK, x.okuma.toFixed(1), x.bolge]
      .join(AYRAC));
  });
  return Buffer.from(s.join("\n") + "\n", "utf8");   // dosya UTF-8 yazilir
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const k = kayitlar(), b = dosya(), satir = b.toString("utf8").trimEnd().split("\n");
  const say = (f) => k.reduce((s, _, i) => s + (f(i) ? 1 : 0), 0);
  console.log(`tohum ${TOHUM}; mantiksal kayit ${k.length}, farkli abone ` +
    `${new Set(k.map((x) => x.abone)).size}, farkli abone-donem ` +
    `${new Set(k.map((x) => x.abone + x.donem)).size}`);
  console.log(`dosya ${b.length} bayt, ${satir.length} fiziksel satir (1 baslik + ` +
    `${satir.length - 1} veri satiri)`);
  console.log(`adresinde ayrac olan kayit ${say(AYRACLI)} (${say(SAYIYLA)} tanesi ` +
    `ayractan sonra sayiyla bitiyor), adresinde satir sonu olan kayit ${say(SATIRLI)}`);
  console.log(`dogru okunmus toplam ${k.reduce((s, x) => s + x.okuma, 0).toFixed(1)} m3`);
  const oz = (ad) => Buffer.from(ad, "utf8").toString("hex").match(/../g).join(" ");
  for (const ad of ["kuzey", "güney"])
    console.log(`bolge "${ad}" UTF-8 baytlari: ${oz(ad)} (${Buffer.byteLength(ad)} bayt, ` +
      `${ad.length} karakter)`);
  console.log(satir[0]); console.log(satir[1]); console.log(satir[14]);
}
```

```
tohum 1246; mantiksal kayit 2037, farkli abone 1400, farkli abone-donem 1877
dosya 108219 bayt, 2077 fiziksel satir (1 baslik + 2076 veri satiri)
adresinde ayrac olan kayit 97 (33 tanesi ayractan sonra sayiyla bitiyor), adresinde satir sonu olan kayit 39
dogru okunmus toplam 42645.6 m3
bolge "kuzey" UTF-8 baytlari: 6b 75 7a 65 79 (5 bayt, 5 karakter)
bolge "güney" UTF-8 baytlari: 67 c3 bc 6e 65 79 (6 bayt, 5 karakter)
abone_no;sayac_no;donem;adres;okuma_m3;bolge
10000;S-4000;2024-01;"Cinar Sok No 12; 45";9.4;kuzey
10013;S-4013;2024-01;"Dere Sok No 25
```

İlk çelişki son üç satırda duruyor. Dosyada **2.076 veri satırı** var ama **2.037 mantıksal
kayıt**; aradaki 39 fark adresinde satır sonu taşıyan 39 kayıttan geliyor. Bir kaydın kaç satır
kapladığı sabit değildir; dosyayı satır sayarak ölçmek yanlış bir sayı verir.

İkinci çelişki ilk veri satırındadır. Adres alanı `"Cinar Sok No 12; 45"` yazıyor: alanın içinde
ayracın kendisi geçiyor. Ayraç, alanları ayırmak için seçilmiş bir karakterdir ve veride de
geçebilir; tırnak tam bu yüzden vardır. Tırnağı okumayan bir ayrıştırıcı burada altı yerine yedi
alan görür ve okuma alanı olarak adresin kuyruğunu okur.

Üçüncüsü bayt satırlarındadır. `kuzey` beş karakter ve beş bayttır, `güney` beş karakter ama altı
bayt. Dosyanın hangi kodlamayla yazıldığı dosyanın içinde yazmaz; okuyan taraf bir karar verir ve
karar yanlışsa hiçbir hata çıkmaz, yalnız harfler değişir.

## Giren, Çıkan, Sessizce Düşen

```js
// oku.mjs — ayni dosya iki ayristirma karariyla okunur. K1 satiri satir sonundan,
// alani ayractan boler, tirnaklari siler ve dosyayi tek baytlik bir kodlamayla
// cozer. K2 tirnak icini korur, dosyayi yazildigi kodlamayla cozer. Ikisi de
// hata vermez; fark yalnizca sayilarda gorunur.
import { dosya, BASLIK, BOLGE, AYRAC, TIRNAK, kayitlar } from "./kaynak.mjs";
const b = dosya(), OK = BASLIK.indexOf("okuma_m3"), BL = BASLIK.indexOf("bolge");

const ayikla = (satirlar) => {
  const cikan = [], dusen = [];
  for (const alan of satirlar) {
    const okuma = Number(alan[OK]);
    if (alan[OK] !== "" && alan[OK] !== undefined && Number.isFinite(okuma))
      cikan.push({ okuma, bolge: alan[BL] });
    else dusen.push(alan.length);
  }
  return { giren: satirlar.length, cikan, dusen };
};

// K1: naif okuyucu. Tirnak diye bir sey bilmez; alan basina tirnaklari siler.
const k1 = (bayt) => ayikla(bayt.toString("latin1").trimEnd().split("\n").slice(1)
  .map((s) => s.split(AYRAC).map((a) => a.replaceAll(TIRNAK, ""))));

// K2: tirnak icini koruyan durum makinesi. Tirnak icinde ayrac da satir sonu da
// alanin parcasidir; yan yana iki tirnak tek tirnak demektir.
function k2(bayt) {
  const m = bayt.toString("utf8"), kayit = []; let alan = [""], ic = false;
  for (let i = 0; i < m.length; i++) {
    const c = m[i];
    if (ic) { if (c === TIRNAK && m[i + 1] === TIRNAK) { alan[alan.length - 1] += c; i++; }
      else if (c === TIRNAK) ic = false; else alan[alan.length - 1] += c; }
    else if (c === TIRNAK) ic = true;
    else if (c === AYRAC) alan.push("");
    else if (c === "\n") { kayit.push(alan); alan = [""]; }
    else alan[alan.length - 1] += c;
  }
  if (alan.length > 1 || alan[0] !== "") kayit.push(alan);
  return ayikla(kayit.slice(1));
}

// Iki toplamin ayni sayilmasi icin esik 0.05 m3'tur: okumalar bir ondalikla
// raporlaniyor, altindaki fark yalnizca yuvarlamadan gelebilir.
const ESIK = 0.05, ENBUYUK = 37.0;   // uretecin verebilecegi en buyuk okuma
const hedef = kayitlar().reduce((s, x) => s + x.okuma, 0);
const y = (w) => (h) => h.map((v, j) => (j ? String(v).padStart(w[j])
  : String(v).padEnd(w[0]))).join("");
const y1 = y([22, 8, 8, 8, 12, 11, 10]);
console.log(y1(["ayristirma karari", "giren", "cikan", "dusen", "toplam m3",
  "eslesmeyen", "kapi no"]));
const sonuc = {};
for (const [ad, f] of [["K1 naif bolme", k1], ["K2 tirnagi koruyan", k2]]) {
  const r = sonuc[ad] = f(b), t = r.cikan.reduce((s, x) => s + x.okuma, 0);
  console.log(y1([ad, r.giren, r.cikan.length, r.dusen.length, t.toFixed(1),
    r.cikan.filter((x) => !BOLGE.includes(x.bolge)).length,
    r.cikan.filter((x) => x.okuma > ENBUYUK).length]));
}
const r1 = sonuc["K1 naif bolme"], t1 = r1.cikan.reduce((s, x) => s + x.okuma, 0);
const yarim = r1.dusen.filter((n) => n < 6).length, kayik = r1.dusen.filter((n) => n > 6).length;
const kapi = r1.cikan.filter((x) => x.okuma > ENBUYUK);
console.log(`\ndosyada ${r1.giren} veri satiri var ama ${kayitlar().length} mantiksal kayit: ` +
  `adresinde satir sonu tasiyan 39 kayit ${r1.giren - 2037} fazladan satir uretti`);
console.log(`K1 ${r1.dusen.length} satiri hicbir uyari vermeden dusurdu: ${yarim} bolunmus ` +
  `yarim satir, ${kayik} alani kaymis satir`);
console.log(`K1'in cikardigi ${r1.cikan.length} kaydin ${kapi.length} tanesi hayatta kaldi ` +
  `ama okuma alaninda adresten kayan bir kapi numarasi tasiyor (${kapi[0].okuma} m3 gibi)`);
console.log(`iki toplam ${Math.abs(hedef - t1).toFixed(1)} m3 ayrisiyor, esik ${ESIK} m3'un ` +
  `${Math.round(Math.abs(hedef - t1) / ESIK)} kati; oran yuzde ` +
  `${(100 * Math.abs(hedef - t1) / hedef).toFixed(1)}`);
console.log(`K1'in ${r1.cikan.filter((x) => !BOLGE.includes(x.bolge)).length} kaydinda bolge ` +
  `adi listeyle eslesmiyor; bolge kirilimi bu kayitlari sessizce disarida birakir`);
```

```
ayristirma karari        giren   cikan   dusen   toplam m3 eslesmeyen   kapi no
K1 naif bolme             2076    1934     142     41661.4       1157        33
K2 tirnagi koruyan        2037    2037       0     42645.6          0         0

dosyada 2076 veri satiri var ama 2037 mantiksal kayit: adresinde satir sonu tasiyan 39 kayit 39 fazladan satir uretti
K1 142 satiri hicbir uyari vermeden dusurdu: 78 bolunmus yarim satir, 64 alani kaymis satir
K1'in cikardigi 1934 kaydin 33 tanesi hayatta kaldi ama okuma alaninda adresten kayan bir kapi numarasi tasiyor (45 m3 gibi)
iki toplam 984.2 m3 ayrisiyor, esik 0.05 m3'un 19684 kati; oran yuzde 2.3
K1'in 1157 kaydinda bolge adi listeyle eslesmiyor; bolge kirilimi bu kayitlari sessizce disarida birakir
```

Tablonun iki satırı aynı dosyayı okuyor ve iki farklı küme veriyor. Tırnak içini koruyan okuma
2.037 satırın hepsini çıkarır, hiçbirini düşürmez ve toplamı 42.645,6 m³ bulur. Naif bölme 2.076
satırdan 1.934 kayıt çıkarır: **142 satır kayboldu ve hiçbiri hata vermedi**. İkisi de sıfır çıkış
koduyla bitti.

Kaybın dökümü iki kalemdir. **78 satır bölünmüş yarımdır**: adresinde satır sonu taşıyan 39 kayıt
dosyada iki satıra yayılmıştı, naif okuyucu ikisini de ayrı satır sandı, ikisinde de okuma alanı
eksik kaldı ve ikisini de attı. Bir kayıt kaybolmak için iki satır harcadı. **64 satır alanı
kaymış satırdır**: adresinde ayraç taşıyan 97 kaydın 64'ünde okuma alanına adres kuyruğu geldi,
sayıya dönüşmedi ve satır düştü. Mantıksal kayıt cinsinden hesap kapanır: 2.037 kayıttan 103'ü
kümeden tamamen çıktı.

Kalan 33 kayıt dersin asıl bulgusudur. Adresinde ayraç taşıyan kayıtların üçte birinde ayraçtan
sonra çıplak bir kapı numarası vardı; kayan alan sayıya dönüştü ve kayıt **hayatta kaldı**. Okuma
alanında artık 45 m³ gibi bir değer duruyor ve VT5'e göre bu değer okuma olamaz. Bu 33 kayıt hiçbir
sayımda eksik görünmez, yalnız toplamı şişirir. Düşen 103 kayıt toplamı azaltır, şişen 33 kayıt
artırır; net fark 984,2 m³, eşiğin 19.684 katı ve toplamın yüzde 2,3'ü olur.

Son satır dördüncü bir kanalı ölçüyor. Dosya UTF-8 yazıldı, naif okuyucu tek baytlık bir kodlamayla
çözdü ve çok baytlı harf taşıyan bölge adları başka dizgelere dönüştü. 1.934 kaydın **1.157'sinde**
bölge adı listeyle eşleşmiyor; bölge kırılımı alan bir özet bu kayıtları sessizce dışarıda bırakır
ve kırılımın toplamı genel toplamı tutmaz. Kodlama kararı tek satır düşürmedi, yalnız kayıtların
yarıdan fazlasını sınıflandırılamaz hale getirdi.

## Ayraç, Belge, Sütun

```js
// bicim.mjs — ayni 2037 kayit uc yapida saklanir. MODELDIR: bicimlerin adlari
// degil yapilari onemli. Sutunlu model: okuma 2 baytlik tamsayi (desi-m3),
// abone 4 baytlik tamsayi, donem ve bolge birer bayt kod, metin uzunluk onekli.
import { dosya, kayitlar, BOLGE } from "./kaynak.mjs";
const k = kayitlar(), belge = Buffer.from(JSON.stringify(k), "utf8");
const metin = (a) => Buffer.concat(a.map((s) => Buffer.from(s, "utf8"))
  .flatMap((x) => [Buffer.from([x.length]), x]));
const SUTUN = {
  abone: Buffer.from(new Int32Array(k.map((x) => x.abone)).buffer),
  okuma: Buffer.from(new Uint16Array(k.map((x) => Math.round(x.okuma * 10))).buffer),
  donem: Buffer.from(k.map((x) => (x.donem === "2024-01" ? 0 : 1))),
  bolge: Buffer.from(k.map((x) => BOLGE.indexOf(x.bolge))),
  sayac: metin(k.map((x) => x.sayac)), adres: metin(k.map((x) => x.adres)),
};
const toplam = () => { let t = 0;
  for (let i = 0; i < k.length; i++) t += SUTUN.okuma.readUInt16LE(i * 2) / 10;
  return t.toFixed(1); };
const y1 = (h) => h.map((v, j) => (j ? String(v).padStart([0, 13, 15, 12][j])
  : String(v).padEnd(22))).join("");
console.log(y1(["yapi", "toplam bayt", "soru icin bayt", "toplam m3"]));
for (const [ad, tam, soru] of [["ayracli metin", dosya().length, dosya().length],
  ["ic ice belge", belge.length, belge.length],
  ["sutunlu", Object.values(SUTUN).reduce((s, x) => s + x.length, 0), SUTUN.okuma.length]])
  console.log(y1([ad, tam, soru, toplam()]));
console.log(`toplam okumayi ogrenmek sutunlu yapida ${SUTUN.okuma.length} bayt, ayracli ` +
  `metinde ${dosya().length} bayt okumak demek: ${(dosya().length / SUTUN.okuma.length)
    .toFixed(0)} kat`);
```

```
yapi                    toplam bayt soru icin bayt   toplam m3
ayracli metin                108219         108219     42645.6
ic ice belge                 220921         220921     42645.6
sutunlu                       68040           4074     42645.6
toplam okumayi ogrenmek sutunlu yapida 4074 bayt, ayracli metinde 108219 bayt okumak demek: 27 kat
```

Üç yapı aynı 2.037 kaydı taşıyor ve aynı toplamı veriyor. Fark, kaydın sınırının nerede
yazıldığındadır. Ayraçlı metinde sınır bir karakterdir ve o karakter veride de geçebilir; 142
satırlık kayıp bunun bedelidir. İç içe belge yapısında alan adı her kayıtta tekrarlandığı için dosya
iki kat büyür, ama kaydın nerede bittiği metnin yapısından okunur. Sütunlu yapıda her sütun ayrı bir
bayt dizisidir ve tipi ilan edilmiştir; okuma alanına metin kayması yapısal olarak mümkün değildir.

Son satır seçimin ikinci yüzünü ölçüyor. Toplam okuma sorusu sütunlu yapıda 4.074 bayt, ayraçlı
metinde 108.219 bayt okumak ister — **27 kat**. Sütunlu yapı bunu ucuza yapar, çünkü adres sütununu
hiç açmaz; buna karşılık tek bir kaydın bütün alanlarını görmek altı ayrı diziye gitmek demektir.
Biçim seçimi bir kolaylık tercihi değil, hangi işin ucuza yapılacağının kararıdır.

## Özet

- Dosyada 2.076 veri satırı, kümede 2.037 mantıksal kayıt vardır; satır saymak kayıt saymak değildir.
- Tırnak içini koruyan okuma 2.037 kaydın hepsini çıkarır ve toplamı 42.645,6 m³ bulur; naif bölme
  1.934 kayıt çıkarır ve 142 satırı hiçbir uyarı vermeden düşürür.
- Düşenin dökümü 78 bölünmüş yarım satır ile 64 alanı kaymış satırdır; 2.037 kayıttan 103'ü kümeden
  çıkmış, kayan alanların 33'ü sayıya dönüşüp okuma alanında kapı numarasıyla hayatta kalmıştır. Net
  fark 984,2 m³, eşiğin 19.684 katıdır.
- Yanlış kodlama tek satır düşürmez ama 1.157 kaydın bölge adını listeyle eşleşmez hale getirir.
- Aynı küme sütunlu yapıda 68.040 bayt tutar ve toplam okuma sorusu 4.074 bayt okunarak cevaplanır;
  ayraçlı metinde aynı soru 108.219 bayt ister.

## Sonraki Adım

Bu ders veriyi bir dosyadan aldı ve satırlarını saydı. Ama verinin çoğu bir veritabanında durur ve
oradan çıkarken dosyanın taşımadığı bir soru getirir: sorgu satır döndürür, fakat o satırın **neyin
bir kaydı** olduğu sorgunun yazımına bağlıdır. Sonraki ders
sorgunun döndürdüğü satırın çözümlemenin sorduğu birimle uyuşup uyuşmadığını ölçer ve iki tablonun
birleştirilmesinin satır sayısını nasıl çoğalttığını sayar.
