CSV, kimsenin sevmediği ama herkesin kullandığı formatlardan biridir. 1970'lerden beri var, hâlâ gezegendeki her elektronik tablo uygulamasının varsayılan dışa aktarma formatı ve şu anda muhtemelen veri iş akışınızın bir yerinde bulunuyor. Format önemsiz derecede basit görünür — sadece virgülle ayrılmış değerler — ancak doğru şekilde ayrıştırmak şaşırtıcı derecede kolay yanlış yapılabilir. Nasıl gerçekten çalıştığını, nerede zorlaştığını ve Python ile JavaScript'te kendinize zarar vermeden nasıl işleyeceğinizi inceleyelim.

CSV Gerçekte Nedir

CSV, Virgülle Ayrılmış Değerler (Comma-Separated Values) anlamına gelir. Bir CSV dosyası düz metin — ikili kodlama yok, meta veri yok, satırlar ve sütunların ötesinde yapı yok. Her satır bir kayıttır, satırdaki her değer bir ayırıcıyla (genellikle virgül) ayrılır ve ilk satır geleneksel olarak sütun adlarını içeren başlık satırıdır. Gerçek bir CSV şöyle görünür:

text
order_id,customer,product,quantity,unit_price,shipped
1001,Alice Nguyen,USB-C Hub,2,34.99,true
1002,Bob Martinez,Mechanical Keyboard,1,129.00,false
1003,Alice Nguyen,HDMI Cable,3,12.50,true
1004,Carol Smith,Webcam 1080p,1,79.95,false

Hepsi bu — altı sütun, dört veri satırı ve bir başlık. Açılı ayraç yok, kaşlı ayraç yok, henüz tırnak işareti gerekmiyor. Bu basitlik tam da CSV'nin devam etmesinin nedenidir: yeryüzündeki hemen her araç onu açabilir, okuyabilir ve üretebilir. Excel, Google Sheets, PostgreSQL'in COPY komutu, pandas, R'nin read.csv()'si — hepsi kutudan çıkar çıkmaz CSV desteğiyle gelir. Ödünleşim, formatın neredeyse hiç yapısı olmamasıdır: tür yok, iç içe geçme yok, şema yok. Siz aksi kararlaştırana kadar her değer bir dizedir.

RFC 4180 — Gerçekten Uygulanmayan Standart

Bir spesifikasyon var: RFC 4180, 2005'te yayımlandı. CRLF satır sonları, çift tırnak kaçışı ve gömülü virgüllerin nasıl işleneceği gibi konuları tanımlar. Ancak şu var — RFC 4180 bilgilendiricidir, standart değildir. Zaten yaygın olan uygulamayı açıklar, kanunlaştırmaz. Kimse onu izlemek zorunda değildir ve uygulamada hemen hiç kimse tam olarak izlemez.

Sonuç, CSV diyalekti kaosudur. CRLF yerine LF kullanan dosyalarınız, ilk satırın başlık olup olmadığı belirsiz dosyalarınız, sonda yeni satır olan ve olmayan dosyalarınız, Excel'in başına UTF-8 BOM eklediği dosyalarınız var. CSV hakkındaki Wikipedia makalesi, pratikte değişen her şeyin sağlam bir özetine sahiptir. En güvenli yaklaşım: kendiniz üretmediğiniz bir CSV dosyası hakkında hiçbir şey varsaymayın ve virgülleri naif bir şekilde bölmek yerine her zaman uygun bir ayrıştırıcı kullanın.

Tırnak Kuralları: Değerlerin İçinde Virgül Göründüğünde

"Sadece virgülleri böl" yaklaşımının çöktüğü yer burasıdır. Bir değer virgül içeriyorsa ne olur? Ya da yeni satır? Ya da çift tırnak işareti? RFC 4180 — ve çoğu gerçek ayrıştırıcı — alanı çift tırnak içine alarak bunu işler. İşte kuralların tam seti:

  • Bir alan virgül, yeni satır veya çift tırnak içeriyorsa, tüm alanı çift tırnak içine alın
  • Bir alan çift tırnak içeriyorsa, onu ikiye katlayarak kaçış uygulayın ("")
  • Tırnak içindeki alanlar birden fazla satıra yayılabilir — yeni satır değerin bir parçası olur
  • Tırnak içindeki boşluklar anlamlıdır ve korunmalıdır
text
order_id,customer,notes,unit_price
1005,David Lee,"Wants gift wrapping, express shipping",45.00
1006,Emma Brown,"Said: ""please handle with care""",89.99
1007,Frank Wu,"Address:
123 Main St
Apt 4B",15.50

Bu örnekte: David'in notes'u virgül içeriyor, bu yüzden tırnak içinde. Emma'nın notu çift tırnak içeriyor, bu yüzden dış tırnakların içinde ikiye katlanıyor. Frank'in adresi birden fazla satıra yayılıyor — satır sonları değerin bir parçası. Herhangi bir dildeki naif bir line.split(',') bu üçünü de tamamen mahveder. Bu yüzden gerçek bir ayrıştırıcıya ihtiyaç duyuyorsunuz.

CSV ayrıştırmanın altın kuralı: virgülleri hiçbir zaman manuel olarak bölmeyin. Her zaman özel bir CSV ayrıştırma kütüphanesi kullanın. Tırnak işaretlerini, kaçış dizilerini, çok satırlı alanları ve kodlamayı işler — bunların hiçbirini basit bir split doğru yapamaz.

Ayırıcı Varyantlar: Sekmeler, Noktalı Virgüller, Kanallar

Adına karşın, CSV virgül kullanmak zorunda değildir. Birkaç yaygın varyant farklı ayırıcılar kullanır ve hepsini pratikte göreceksiniz:

  • TSV (Sekmeyle Ayrılmış Değerler) — ayırıcı olarak \t kullanır. Biyoinformatik (BLAST çıktısı, VCF dosyaları), veritabanı dışa aktarmaları ve değerlerin sıklıkla virgül içerdiği her yerde yaygındır
  • Noktalı virgülle ayrılmış — virgülün ondalık ayırıcı olduğu bölgelerde (Almanya, Fransa, AB'nin çoğu) Excel'in varsayılanıdır. Bir Avrupalı meslektaştan CSV alıp tek bir büyük sütun aldıysanız, nedeni budur
  • Kanalla ayrılmış| kullanır. Sekmelerin ana çerçeve sistemleri tarafından kesilebileceği eski bankacılık ve sigorta veri dışa aktarmalarında yaygındır
  • Sabit genişlik — teknik olarak CSV değildir, ancak genellikle aynı kategoriye dahil edilir. Sütunlar sınırlandırılmak yerine sabit genişliklere kadar doldurulur

Çoğu CSV ayrıştırıcı, ayırıcıyı açıkça belirtmenize olanak tanır. Başkalarının tüketeceği bir CSV yazarken ayırıcınızı belgeleyin. Üretmediğiniz birini okurken, varsayım yapmadan önce ilk birkaç satırı kontrol edin. CSV Formatter, standart olmayan ayırıcılara sahip dosyaları incelemenize ve yeniden biçimlendirmenize yardımcı olabilir.

Python'da CSV Ayrıştırma

Python'ın standart kütüphanesi, csv modülünü içerir ve gerçekten iyidir. En sık kullanacağınız iki sınıf, satır-olarak-liste erişimi için csv.reader ve satır-olarak-sözlük erişimi için csv.DictReader'dır (ki bu neredeyse her zaman istediğiniz şeydir).

python
import csv

# csv.reader — each row is a list of strings
with open('orders.csv', 'r', encoding='utf-8', newline='') as f:
    reader = csv.reader(f)
    header = next(reader)          # consume the header row
    for row in reader:
        order_id, customer, product, quantity, price, shipped = row
        print(f"Order {order_id}: {quantity}x {product} for {customer}")

# csv.DictReader — each row is an OrderedDict keyed by the header
with open('orders.csv', 'r', encoding='utf-8', newline='') as f:
    reader = csv.DictReader(f)
    for row in reader:
        if row['shipped'] == 'false':
            print(f"Pending: {row['order_id']} — {row['customer']}")

Her zaman eklenmesi gereken iki şey: encoding='utf-8' (veya dosyanın gerçekte ne kullandığı) ve newline=''. Bu ikincisi kritiktir — csv modülü kendi yeni satır işlemeini yapar ve ham baytlara ihtiyaç duyar. newline='' olmadan, Windows'ta fazladan boş satırlar alabilirsiniz.

Yazma csv.writer ile aynı derecede basittir:

python
import csv

orders = [
    {'order_id': 1008, 'customer': 'Grace Kim', 'product': 'Laptop Stand', 'quantity': 1, 'unit_price': 49.99, 'shipped': False},
    {'order_id': 1009, 'customer': 'Henry Park', 'product': 'USB-C Hub', 'quantity': 2, 'unit_price': 34.99, 'shipped': True},
]

with open('new_orders.csv', 'w', encoding='utf-8', newline='') as f:
    fieldnames = ['order_id', 'customer', 'product', 'quantity', 'unit_price', 'shipped']
    writer = csv.DictWriter(f, fieldnames=fieldnames)

    writer.writeheader()
    writer.writerows(orders)

# To use a different delimiter (e.g. tab-separated)
with open('new_orders.tsv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.writer(f, delimiter='\t')
    writer.writerow(['order_id', 'customer', 'product'])
    writer.writerow([1008, 'Grace Kim', 'Laptop Stand'])

csv modülü tüm tırnakları otomatik olarak işler — bir alan virgül veya yeni satır içeriyorsa, bunu düşünmenize gerek kalmadan çift tırnak içine alır. Kütüphane kullanmanın tüm amacı budur.

JavaScript / Node.js'de CSV Ayrıştırma

JavaScript'in yerleşik bir CSV ayrıştırıcısı yoktur. Cazip olan şu:

js
// ❌ Don't do this — breaks immediately on quoted fields
const rows = csvText.split('\n').map(line => line.split(','));

Herhangi bir değer virgül, tırnak içinde yeni satır veya kaçış uygulanan çift tırnak içerdiği anda bu başarısız olur. Gerçek bir şey için kütüphane kullanın. PapaParse en iyi seçimdir — hızlıdır, tüm uç durumları işler, hem tarayıcıda hem de Node.js'de çalışır ve büyük dosyalar için akış desteği sunar.

js
import Papa from 'papaparse';
import fs from 'fs';

// Parse a CSV string
const csvText = fs.readFileSync('orders.csv', 'utf8');

const result = Papa.parse(csvText, {
  header: true,          // first row becomes object keys
  skipEmptyLines: true,  // ignore blank lines at end of file
  dynamicTyping: true,   // converts "true"/"false" to booleans, numbers to numbers
});

console.log(result.data);
// [
//   { order_id: 1001, customer: 'Alice Nguyen', product: 'USB-C Hub', quantity: 2, unit_price: 34.99, shipped: true },
//   { order_id: 1002, customer: 'Bob Martinez', product: 'Mechanical Keyboard', quantity: 1, unit_price: 129, shipped: false },
//   ...
// ]

// Check for parse errors
if (result.errors.length > 0) {
  console.error('Parse errors:', result.errors);
}

// Generate CSV from an array of objects
const orders = [
  { order_id: 1008, customer: 'Grace Kim', product: 'Laptop Stand', quantity: 1, unit_price: 49.99 },
  { order_id: 1009, customer: 'Henry Park', product: 'USB-C Hub', quantity: 2, unit_price: 34.99 },
];

const csvOutput = Papa.unparse(orders);
fs.writeFileSync('export.csv', csvOutput, 'utf8');

dynamicTyping seçeneği kullanışlıdır ancak bilmeye değer — "34.99" gibi şeyleri otomatik olarak sayıya dönüştürür. Bu genellikle istediğiniz şeydir, ancak order_id gibi bir alan CSV'de sayı olduğunda ama onu dize olarak istediğinizde sizi şaşırtabilir. Katı dize çıktısına ihtiyacınız varsa kapatın.

CSV ve diğer formatlar arasında hızlı dönüşümler için CSV'den JSON'a aracı, kod yazmadan tarayıcıda en yaygın durumları işler — tek seferlik veri dönüşümleri için kullanışlıdır. Veriyi XML tabanlı bir sisteme beslemeniz gerekiyorsa CSV'den XML'e de mevcuttur.

Kesinlikle Karşılaşacağınız Yaygın Tuzaklar

İyi bir ayrıştırıcıyla bile, CSV'nin birkaç iyi bilinen mayını vardır. İşte en sık karşılaşılanlar:

  • Excel'den BOM baytları. Excel bir CSV'yi "UTF-8" olarak dışa aktardığında, genellikle başına UTF-8 BOM (EF BB BF onaltılı veya karakter olarak \ufeff) ekler. Bu, ilk sütun başlığının order_id yerine order_id gibi görünmesine neden olur. Python'da bunu otomatik olarak kaldırmak için dosyayı utf-8 yerine encoding='utf-8-sig' ile açın. PapaParse bunu şeffaf bir şekilde işler.
  • CRLF vs LF satır sonları. RFC 4180 CRLF (\r\n) belirtir, ancak Unix araçları LF (\n) üretir ve eski Mac dosyaları yalnızca CR (\r) kullanır. Bu nedenle Python'ın csv modülü newline='' gerektirir — üçünü de dahili olarak işler. Ham baytları okuyup manuel olarak bölüyorsanız, \r'yi açıkça kaldırmanız gerekir.
  • Kodlama sorunları. CSV, HTML'nin <meta charset>'i veya XML'nin <?xml encoding="..."?>'i gibi kendi kodlamasını beyan etmenin bir yoluna sahip değildir. Excel dosyaları genellikle UTF-8 yerine Windows-1252'de (a.k.a. CP1252) kaydeder, bu da aksan işaretli karakterleri bozar. é yerine é gibi karakterler görüyorsanız, bir UTF-8 dosyasının Latin-1 olarak çözümlendiği veya tam tersi bir durum söz konusudur. Kodlamayı her zaman dosyayı üreten kişiyle bant dışında belirleyin.
  • Tarih gibi görünen sayılar. Excel açarken veya kaydederken 1-2 veya 03/04 gibi değerleri sessizce tarihlere dönüştürür. Ürün kodlarını veya sürüm numaralarını dışa aktarıyorsanız, bunu önlemek için Excel'de başına tek tırnak ekleyin ('1-2) — veya dosyayı üreten kişiye bunu yapmasını söyleyin.
  • Sondaki virgüller. Bazı dışa aktarıcılar her satırın sonuna sondaki virgülü ekler, bu da hayalet boş bir sütun oluşturur. Sağlam bir ayrıştırıcı bunu görmezden gelir; naif bir bölme fazladan boş bir dize öğesi oluşturur.

Garip görünen bir dosyayla çalışıyorsanız, CSV Doğrulayıcı, dosyayı işlemeye çalışmadan önce iyi biçimlendirilmiş olup olmadığını ve kodlama veya yapısal sorunları hızlıca söyleyebilir.

CSV vs JSON vs Excel — Ne Zaman Ne Kullanılır

Bu üç format pratikte çok örtüşür, ancak her birinin net bir avantajı vardır:

  • CSV kullanın sistemler arasında düz, tablo halindeki verileri taşırken — veritabanı dışa aktarmaları, analitik ardışık düzenler, elektronik tablo içe aktarmaları, toplu veri yükleme. Evrensel olarak desteklenir, boyut olarak küçüktür ve git'te önemsiz şekilde karşılaştırılabilir. Kısıtlama: düzdür. İç içe geçme yok, tür yok, ilişki yok.
  • JSON kullanın veri hiyerarşik olduğunda veya şema önemli olduğunda. Birden fazla satır öğesi olan bir sipariş, iç içe geçmiş nesnelerle bir yapılandırma dosyası, bir API yanıtı — bunlar doğal olarak JSON'dır. CSV sizi veriyi normalden çıkmaya veya kendi iç içe geçme kuralınızı icat etmeye zorlar. JSON spesifikasyonu temiz ve net; format türleri korur (sayılar, boolean'lar, null, diziler, nesneler).
  • Excel (.xlsx) kullanın çıktı makineler için değil insanlar içinse. Biçimlendirme, formüller, birden fazla sayfa, grafikler — son tüketici bir iş kullanıcısıysa, Excel genellikle doğru seçimdir. Asla sistemler arasında bir değiş tokuş formatı olarak kullanmayın. OOXML spesifikasyonu çok büyüktür ve format sürümler arasında kırılgandır.

Pratik bir kural var: verileri doğal olarak bir elektronik tabloda görüntülerseniz, CSV muhtemelen uygundur. Bir ağaçta veya kod düzenleyicisinde görüntülerseniz JSON kullanın. Filtreleyip sıralayacak bir iş paydaşına gönderiyorsanız Excel kullanın.

Özet

CSV tasarım gereği basit ve pratikte yanıltıcı şekilde zordur. Formatın uygulanmış bir standardı yoktur, birden fazla ayırıcı varyantında gelir ve her manuel ayrıştırma girişimini bozan tırnak kurallarına dayanır. Düzeltme her zaman aynıdır: gerçek bir ayrıştırıcı kullanın (Python'ın yerleşik csv modülü veya JavaScript'te PapaParse), kodlamayı her zaman açıkça belirtin ve Excel'den BOM baytlarına dikkat edin. Güvenilir bir ayrıştırıcıya sahip olduğunuzda, CSV aslında harika bir formattır — üretmesi hızlı, herhangi bir metin düzenleyicisinde incelemesi kolay ve her yerde desteklenir. CSV dosyalarıyla günlük çalışma için CSV'den JSON'a, CSV Formatter ve CSV Doğrulayıcı araçları tek satır kod yazmadan yaygın işlemleri işler.