Node.js’te CSV Dosyalarını Node-CSV Kullanarak Okuma ve Yazma
CSV (Comma Separated Values) dosyaları, verilerin basit ve taşınabilir bir formatta depolanması ve değiş tokuşu için yaygın olarak kullanılan metin tabanlı dosyalardır. Elektronik tablolar, veri tabanları ve çeşitli uygulamalar arasında veri aktarımında kilit rol oynarlar. Node.js, arka uç sistemleri ve veri işleme görevleri için güçlü bir platform olduğundan, CSV dosyalarını etkili bir şekilde okuyup yazabilmek büyük önem taşır. Bu rehberde, Node.js ekosistemindeki en popüler ve güçlü kütüphanelerden biri olan node-csv‘yi kullanarak CSV dosyalarıyla nasıl çalışılacağını detaylı bir şekilde inceleyeceğiz.
node-csv kütüphanesi, CSV verilerini ayrıştırmak (parse) ve oluşturmak (stringify) için modüler ve akış tabanlı (stream-based) bir yaklaşım sunar. Bu sayede, çok büyük dosyalarda bile bellek verimliliği ve yüksek performans sağlanır. Bu rehberde, node-csv‘nin kurulumundan başlayarak, temel okuma ve yazma işlemlerine, gelişmiş ayarlara ve hata yönetimine kadar geniş bir yelpazede pratik bilgiler ve kod örnekleri bulacaksınız.
Node-CSV Kurulumu ve Temel Kullanım
node-csv kütüphanesini kullanmaya başlamadan önce, Node.js ve npm’in (Node Package Manager) sisteminizde kurulu olduğundan emin olmalısınız. Kurulum tamamlandıktan sonra, yeni bir Node.js projesi oluşturup node-csv paketini projenize ekleyebilirsiniz.
Node.js Ortamınızı Hazırlama
Eğer Node.js kurulu değilse, resmi web sitesinden (nodejs.org) indirip kurabilirsiniz. Kurulumdan sonra, terminalinizde aşağıdaki komutları çalıştırarak bir proje dizini oluşturun ve başlatın:
mkdir csv-rehber
cd csv-rehber
npm init -y
Bu komutlar, projeniz için package.json dosyasını oluşturacaktır.
node-csv Paketini Kurma
Şimdi node-csv paketini projenize kuralım. node-csv aslında dört ana modülden oluşan bir meta-pakettir: csv-parse (okuma için), csv-stringify (yazma için), stream-transform (veri dönüştürme için) ve stream-generate (test verisi üretimi için). Tek bir npm install csv komutuyla hepsi kurulur:
npm install csv
Kurulum tamamlandığında, projeniz node-csv‘nin tüm yeteneklerini kullanmaya hazır hale gelir.
CSV Dosyalarını Okuma (Parsing)
CSV dosyalarını okuma işlemi, node-csv kütüphanesinin csv-parse modülü aracılığıyla gerçekleştirilir. Bu modül, hem callback tabanlı basit okuma işlemleri hem de stream tabanlı büyük dosya işleme yetenekleri sunar.
Temel CSV Okuma İşlemi (Callback Tabanlı)
En basit okuma yöntemi, tüm CSV içeriğini bir string olarak alıp tek seferde ayrıştırmaktır. Bu yöntem, küçük ve orta boyutlu CSV verileri için uygundur.
parse.js adında bir dosya oluşturalım:
const { parse } = require('csv-parse');
const csvString = id,name,email
1,Alice,alice@example.com
2,Bob,bob@example.com
3,Charlie,charlie@example.com;
parse(csvString, {
columns: true, // İlk satırı başlık olarak kullan ve objeler döndür
skip_empty_lines: true // Boş satırları atla
}, (err, records) => {
if (err) {
console.error('CSV ayrıştırma hatası:', err);
return;
}
console.log('Ayrıştırılan kayıtlar (objeler):', records);
});
// Çıktı:
// Ayrıştırılan kayıtlar (objeler): [
// { id: '1', name: 'Alice', email: 'alice@example.com' },
// { id: '2', name: 'Bob', email: 'bob@example.com' },
// { id: '3', name: 'Charlie', email: 'charlie@example.com' }
// ]
parse(csvString, (err, records) => { // columns: true olmadan, array of arrays döner
if (err) {
console.error('CSV ayrıştırma hatası:', err);
return;
}
console.log('Ayrıştırılan kayıtlar (arrayler):', records);
});
// Çıktı:
// Ayrıştırılan kayıtlar (arrayler): [
// [ 'id', 'name', 'email' ],
// [ '1', 'Alice', 'alice@example.com' ],
// [ '2', 'Bob', 'bob@example.com' ],
// [ '3', 'Charlie', 'charlie@example.com' ]
// ]
columns: true seçeneği, CSV dosyasının ilk satırını sütun başlıkları olarak kabul eder ve her kaydı bu başlıklara sahip bir JavaScript objesi olarak döndürür. Bu, verilere isimleriyle erişmeyi çok daha kolay hale getirir. skip_empty_lines: true ise boş satırları otomatik olarak atlar.
Dosyadan CSV Okuma (Stream Tabanlı)
Büyük CSV dosyalarıyla çalışırken, dosyanın tamamını belleğe yüklemek yerine akış (stream) tabanlı bir yaklaşım kullanmak çok daha verimlidir. node-csv‘nin parse modülü, Node.js’in fs.createReadStream ile sorunsuz bir şekilde entegre olur.
Öncelikle, input.csv adında bir dosya oluşturalım:
urun_kodu,urun_adi,fiyat
A101,Laptop,12000
B202,Klavye,850
C303,Fare,300
Şimdi bu dosyayı stream kullanarak okuyalım:
read_stream.js adında bir dosya oluşturalım:
const fs = require('fs');
const { parse } = require('csv-parse');
const records = [];
const parser = parse({
columns: true, // Başlık satırını kullan
delimiter: ',', // Ayırıcı karakter (varsayılan)
skip_empty_lines: true
});
fs.createReadStream('./input.csv')
.pipe(parser)
.on('data', (record) => {
// Her bir kayıt işlendiğinde buraya gelir
records.push(record);
// console.log('Okunan kayıt:', record); // Her kaydı anında görmek için
})
.on('end', () => {
console.log('CSV okuma işlemi tamamlandı.');
console.log('Toplam kayıt sayısı:', records.length);
console.log('Tüm kayıtlar:', records);
})
.on('error', (err) => {
console.error('CSV okuma hatası:', err.message);
});
Bu örnekte, fs.createReadStream ile input.csv dosyasından bir okuma akışı oluşturulur. .pipe(parser) ile bu akış csv-parse ayrıştırıcısına yönlendirilir. parser her bir kaydı işlediğinde data olayını tetikler ve tüm dosya okunduğunda end olayı tetiklenir. Bu sayede dosyanın tamamını belleğe almadan satır satır işleyebiliriz.
Gelişmiş Okuma Ayarları
parse fonksiyonu veya yapıcısı, farklı CSV formatlarını desteklemek için çeşitli seçenekler sunar:
* delimiter: Varsayılan olarak virgül (',') kullanılır. Noktalı virgül (;) veya tab (\t) gibi farklı bir ayırıcı kullanmak için bu seçeneği ayarlayabilirsiniz.
* quote: Tırnak karakteri. Varsayılan '"'.
* escape: Tırnak içinde tırnakları veya ayırıcıları kaçış karakteri. Varsayılan '"'.
* trim: Her alanın başındaki ve sonundaki boşlukları kırpar. Varsayılan false.
* comment: Yorum satırlarını belirten karakter. Varsayılan ''.
Örneğin, noktalı virgül ile ayrılmış bir CSV dosyasını okumak için:
const { parse } = require('csv-parse');
const semicolonCsv = id;name;city
1;Ayşe;Ankara
2;Mehmet;İzmir;
parse(semicolonCsv, {
columns: true,
delimiter: ';', // Ayırıcıyı noktalı virgül olarak belirle
trim: true // Alanlardaki boşlukları kırp
}, (err, records) => {
if (err) {
console.error('Hata:', err);
return;
}
console.log('Noktalı virgül ile ayrıştırılan kayıtlar:', records);
});
CSV Dosyalarını Yazma (Stringifying)
CSV dosyalarına veri yazma işlemi, node-csv kütüphanesinin csv-stringify modülü aracılığıyla gerçekleştirilir. Tıpkı okumada olduğu gibi, yazma işlemi de hem callback tabanlı hem de stream tabanlı yöntemleri destekler.
Temel CSV Yazma İşlemi (Callback Tabanlı)
Küçük veri setlerini CSV formatında bir string’e dönüştürmek için stringify fonksiyonunu kullanabiliriz.
write.js adında bir dosya oluşturalım:
const { stringify } = require('csv-stringify');
const records = [
['Ad', 'Soyad', 'Yaş'],
['Can', 'Yılmaz', 30],
['Deniz', 'Demir', 25]
];
stringify(records, (err, output) => {
if (err) {
console.error('CSV oluşturma hatası:', err);
return;
}
console.log('Oluşturulan CSV stringi:\n', output);
});
// Çıktı:
// Oluşturulan CSV stringi:
// Ad,Soyad,Yaş
// Can,Yılmaz,30
// Deniz,Demir,25
Yukarıdaki örnekte, bir array of arrays’i CSV string’ine dönüştürdük.
Dosyaya CSV Yazma (Stream Tabanlı)
Büyük veri setlerini bir CSV dosyasına yazarken stream’ler kullanmak, bellek tüketimini optimize eder ve performansı artırır.
output.csv dosyasına yazmak için write_stream.js adında bir dosya oluşturalım:
const fs = require('fs');
const { stringify } = require('csv-stringify');
const dataToWrite = [
{ id: 1, product: 'Klavye', price: 850 },
{ id: 2, product: 'Mouse', price: 300 },
{ id: 3, product: 'Monitör', price: 2500 }
];
const stringifier = stringify({
header: true, // İlk satıra başlıkları ekle
columns: ['id', 'product', 'price'], // Sütun sırasını ve isimlerini belirle
delimiter: ';' // Ayırıcıyı noktalı virgül yapalım
});
const writableStream = fs.createWriteStream('./output.csv');
writableStream.on('finish', () => {
console.log('CSV dosyası başarıyla yazıldı: output.csv');
});
stringifier.pipe(writableStream);
dataToWrite.forEach(record => {
stringifier.write(record);
});
stringifier.end(); // Yazma işlemini sonlandır
Bu örnekte, bir dizi objeyi output.csv dosyasına yazdık. header: true seçeneği ile objelerin anahtarlarını başlık olarak ekledik. columns seçeneği ile de sütunların sırasını ve hangi objelerin hangi anahtarlarının sütun olacağını belirledik. delimiter: ';' ile ayırıcıyı noktalı virgül olarak ayarladık.
Gelişmiş Yazma Ayarları
stringify fonksiyonu veya yapıcısı, CSV çıktısını özelleştirmek için çeşitli seçenekler sunar:
* header: true ise, ilk satıra sütun başlıklarını ekler. Varsayılan false.
* columns: Çıktıda görünecek sütunları ve sıralarını belirten bir dizi. Objelerle çalışırken çok kullanışlıdır.
* quote: Alanları çevreleyen tırnak karakteri. Varsayılan '"'.
* escape: Tırnak içinde tırnakları veya ayırıcıları kaçış karakteri. Varsayılan '"'.
* eof: Dosyanın sonuna yeni bir satır karakteri eklenip eklenmeyeceği. Varsayılan true.
Örneğin, belirli sütunları belirli bir sırada ve farklı bir tırnak karakteriyle yazmak için:
const { stringify } = require('csv-stringify');
const users = [
{ id: 1, name: 'Ayşe', email: 'ayse@example.com', city: 'İstanbul' },
{ id: 2, name: 'Fatma', email: 'fatma@example.com', city: 'Ankara' }
];
stringify(users, {
header: true,
columns: ['name', 'email'], // Sadece 'name' ve 'email' sütunlarını yaz
quote: "'" // Tek tırnak kullan
}, (err, output) => {
if (err) {
console.error('Hata:', err);
return;
}
console.log('Özelleştirilmiş CSV çıktısı:\n', output);
});
// Çıktı:
// Özelleştirilmiş CSV çıktısı:
// 'name','email'
// 'Ayşe','ayse@example.com'
// 'Fatma','fatma@example.com'
Veri Dönüştürme ve Hata Yönetimi
node-csv kütüphanesi, okuma ve yazma işlemleri arasında veriyi dönüştürme ve olası hataları yönetme konusunda da güçlü mekanizmalar sunar.
Veri Dönüştürme (stream-transform)
node-csv‘nin stream-transform modülü, okunan veriyi yazmadan önce değiştirmek için kullanılır. Bu, verileri temizlemek, formatlamak veya zenginleştirmek için idealdir.
transform.js adında bir dosya oluşturalım. Bu örnekte input.csv dosyasındaki verileri okuyup, fiyat sütununu KDV ekleyerek output_transformed.csv dosyasına yazacağız.
const fs = require('fs');
const { parse } = require('csv-parse');
const { transform } = require('stream-transform');
const { stringify } = require('csv-stringify');
// input.csv dosyasını okuma akışı
const readStream = fs.createReadStream('./input.csv');
// output_transformed.csv dosyasına yazma akışı
const writeStream = fs.createWriteStream('./output_transformed.csv');
// CSV ayrıştırıcı (parser)
const parser = parse({
columns: true,
delimiter: ','
});
// Veri dönüştürme (transformer)
const transformer = transform((record, callback) => {
// Her kayıt için bu fonksiyon çalışır
const originalPrice = parseFloat(record.fiyat);
const priceWithKdv = (originalPrice * 1.18).toFixed(2); // %18 KDV ekle
record.fiyat = priceWithKdv; // Fiyatı güncelle
record.kdvli_fiyat = priceWithKdv; // Yeni bir sütun ekleyebiliriz
callback(null, record); // Dönüştürülmüş kaydı geri döndür
}, { parallel: 10 }); // Paralel işleme sayısı
// CSV oluşturucu (stringifier)
const stringifier = stringify({
header: true,
columns: ['urun_kodu', 'urun_adi', 'fiyat', 'kdvli_fiyat'] // Sütun sırasını belirle
});
// Akışları birbirine bağla: Oku -> Ayrıştır -> Dönüştür -> Oluştur -> Yaz
readStream
.pipe(parser)
.pipe(transformer)
.pipe(stringifier)
.pipe(writeStream)
.on('finish', () => {
console.log('Veri dönüştürme ve yazma işlemi tamamlandı: output_transformed.csv');
})
.on('error', (err) => {
console.error('Akış hatası:', err.message);
});
Bu örnekte, input.csv dosyasındaki ürün fiyatlarına %18 KDV ekleyip, yeni bir kdvli_fiyat sütunuyla output_transformed.csv dosyasına yazdık. stream-transform modülü, akışlar arasında esnek veri manipülasyonu sağlar.
Hata Yönetimi
Akış tabanlı işlemlerde hata yönetimi kritiktir. Node.js akışları, error olayını tetikleyerek hataları bildirir. Bu olayları dinleyerek hataları yakalayabilir ve uygulamanızın çökmesini engelleyebilirsiniz.
Yukarıdaki read_stream.js örneğindeki gibi, on('error', (err) => { ... }) kalıbını kullanarak tüm akışlarda hata yönetimi yapmanız önemlidir. Örneğin, dosya okuma hatası, geçersiz CSV formatı gibi durumlar bu şekilde ele alınabilir.
const fs = require('fs');
const { parse } = require('csv-parse');
const parser = parse({ columns: true });
fs.createReadStream('./non_existent_file.csv') // Mevcut olmayan bir dosya
.pipe(parser)
.on('data', (record) => {
console.log(record);
})
.on('end', () => {
console.log('Okuma tamamlandı.');
})
.on('error', (err) => {
// Dosya bulunamadığında bu hata yakalanır
console.error('Akış hatası:', err.message);
if (err.code === 'ENOENT') {
console.error('Belirtilen dosya bulunamadı.');
}
});
// Geçersiz CSV formatı (örneğin, tırnak işaretleri eşleşmiyor)
const malformedCsv = "id","name
1,"Alice";
parse(malformedCsv, { columns: true }, (err, records) => {
if (err) {
console.error('Geçersiz CSV ayrıştırma hatası:', err.message);
return;
}
console.log(records);
});
Callback tabanlı işlemlerde ise, callback fonksiyonuna gelen ilk argüman (err) hata objesini içerir ve bu argümanı kontrol ederek hataları yönetebilirsiniz.
Sonuç ve Sıkça Sorulan Sorular (SSS)
Bu rehberde, Node.js ortamında node-csv kütüphanesini kullanarak CSV dosyalarını nasıl okuyup yazacağınızı detaylı bir şekilde öğrendiniz. node-csv‘nin modüler yapısı ve akış tabanlı yaklaşımı sayesinde, küçük veri parçacıklarından gigabaytlarca büyüklükteki dosyalara kadar her türlü CSV işleme ihtiyacınız için performanslı ve esnek çözümler geliştirebilirsiniz. Özellikle büyük dosyalarla çalışırken akışların kullanılması, uygulamanızın bellek verimliliği ve genel performansı açısından kritik öneme sahiptir.
Sıkça Sorulan Sorular (SSS)
node-csv neden diğer CSV kütüphanelerinden daha iyi?
node-csv, akış tabanlı yapısıyla öne çıkar. Bu, çok büyük CSV dosyalarını bile belleğe yüklemeden, parça parça işleyebilme yeteneği sağlar. Ayrıca modüler yapısı (parse, stringify, transform), sadece ihtiyacınız olan işlevselliği kullanmanıza olanak tanır ve genel olarak yüksek performans sunar.
Çok büyük CSV dosyalarıyla nasıl başa çıkılır?
Akış (stream) tabanlı yöntemleri kullanmalısınız. fs.createReadStream ile dosyayı okuma akışına dönüştürüp, csv-parse‘a yönlendirerek bellekteki yükü minimumda tutabilirsiniz. Aynı şekilde, yazma işlemlerinde csv-stringify‘ı fs.createWriteStream ile birlikte kullanmalısınız.
CSV dosyasında farklı ayırıcılar (delimiter) varsa ne yapmalıyım?
parse ve stringify fonksiyonlarının delimiter seçeneğini kullanarak virgül (,) dışındaki ayırıcıları (örneğin noktalı virgül (;), tab (\t)) belirtebilirsiniz.
Türkçe karakter (UTF-8) desteği var mı?
Evet, Node.js ve node-csv varsayılan olarak UTF-8 kodlamasını destekler. Dosyalarınız UTF-8 olarak kaydedildiği sürece Türkçe karakterlerle ilgili herhangi bir sorun yaşamazsınız. Ancak, farklı bir kodlamayla kaydedilmiş bir dosya okuyorsanız, fs.createReadStream içinde encoding seçeneğini ayarlamanız gerekebilir (örneğin, fs.createReadStream('file.csv', { encoding: 'latin1' })).
CSV içinde tırnak işaretleri veya özel karakterler varsa nasıl işlenir?
node-csv kütüphanesi, CSV standartlarına uygun olarak tırnak işaretleri (quote) ve kaçış karakterleri (escape) ile özel karakterleri otomatik olarak işler. Eğer bir alanın içinde ayırıcı karakter veya tırnak işareti varsa, bu alan otomatik olarak tırnak içine alınır ve içindeki tırnak işaretleri kaçış karakteriyle ("" veya \") işlenir. Bu davranış quote ve escape seçenekleriyle özelleştirilebilir.
Umarız bu kapsamlı rehber, Node.js projelerinizde CSV dosyalarıyla çalışırken size yardımcı olur!