CSV is een van die formaten die niemand leuk vindt maar iedereen gebruikt. Het bestaat al sinds de jaren 70, het is nog steeds het standaard exportformaat voor elke spreadsheet-app op de planeet, en het zit bijna zeker ergens in je datapijplijn op dit moment. Het formaat lijkt triviaal eenvoudig — gewoon waarden gescheiden door komma's — maar het correct parsen is verrassend gemakkelijk fout te doen. Laten we doorlopen hoe het eigenlijk werkt, waar het lastig wordt en hoe je het in Python en JavaScript aanpakt zonder jezelf in de voet te schieten.

Wat CSV Eigenlijk Is

CSV staat voor Comma-Separated Values (door komma's gescheiden waarden). Een CSV-bestand is platte tekst — geen binaire codering, geen metadata, geen structuur buiten rijen en kolommen. Elke regel is een record, elke waarde binnen een regel wordt gescheiden door een scheidingsteken (meestal een komma), en de eerste rij is conventioneel een headerrij met kolomnamen. Zo ziet een echte CSV eruit:

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

Dat is het — zes kolommen, vier datarijen en een header. Geen punthaakjes, geen accolades, geen aanhalingstekens nodig (nog niet). Deze eenvoud is precies waarom CSV blijft bestaan: bijna elk hulpmiddel op aarde kan het openen, lezen en produceren. Excel, Google Sheets, PostgreSQL's COPY-commando, pandas, R's read.csv() — allemaal CSV direct klaar voor gebruik. De afweging is dat het formaat bijna geen structuur heeft: geen typen, geen nesting, geen schema. Elke waarde is een string totdat je anders besluit.

RFC 4180 — De Standaard die Niet Echt Gehandhaafd Wordt

Er is een specificatie: RFC 4180, gepubliceerd in 2005. Het definieert zaken zoals CRLF-regeleindes, escape voor dubbele aanhalingstekens en hoe ingesloten komma's worden afgehandeld. Maar hier is het ding — RFC 4180 is informatief, geen standaard. Het beschrijft wat al gangbare praktijk was, niet wat voorgeschreven wordt. Niemand is verplicht het te volgen, en in de praktijk volgt bijna niemand het precies.

Het resultaat is CSV-dialectchaos. Je hebt bestanden die LF gebruiken in plaats van CRLF, bestanden waarbij de eerste rij al dan niet een header is, bestanden met een afsluitende nieuwe regel en bestanden zonder, bestanden met een UTF-8 BOM toegevoegd door Excel. Het Wikipedia-artikel over CSV heeft een goede samenvatting van alles wat in de praktijk varieert. De veiligste aanpak: ga nooit iets aannemen over een CSV-bestand dat je niet zelf hebt gemaakt, en gebruik altijd een goede parser in plaats van naïef op komma's te splitsen.

Aanhalingstekensregels: Wanneer Komma's in Waarden Voorkomen

Hier valt "gewoon op komma's splitsen" uit elkaar. Wat gebeurt er als een waarde een komma bevat? Of een nieuwe regel? Of een dubbel aanhalingsteken? RFC 4180 — en de meeste echte parsers — handelen dit af door het veld in dubbele aanhalingstekens te zetten. Hier is de volledige set regels:

  • Als een veld een komma, een nieuwe regel of een dubbel aanhalingsteken bevat, zet het gehele veld dan tussen dubbele aanhalingstekens
  • Als een veld een dubbel aanhalingsteken bevat, escape het dan door het te verdubbelen ("")
  • Velden tussen aanhalingstekens kunnen meerdere regels beslaan — de nieuwe regel wordt deel van de waarde
  • Witruimte binnen aanhalingstekens is significant en moet bewaard blijven
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

In dat voorbeeld: Davids notes bevat een komma, dus staat het tussen aanhalingstekens. Emma's notitie bevat dubbele aanhalingstekens, dus worden ze verdubbeld binnen de buitenste aanhalingstekens. Franks adres beslaat meerdere regels — de regelafbrekingen zijn deel van de waarde. Een naïeve line.split(',') in elke taal zal alle drie deze gevallen volledig verknoeien. Daarom heb je een echte parser nodig.

De gouden regel van CSV-parsering: splits nooit handmatig op komma's. Gebruik altijd een speciale CSV-parserbibliotheek. Die verwerkt aanhalingstekens, escape-sequenties, meerregelige velden en codering — geen van welke een eenvoudige split correct kan doen.

Scheidingstekenvarianten: Tabs, Puntkomma's, Pipes

Ondanks de naam hoeft CSV geen komma's te gebruiken. Verschillende veelvoorkomende varianten gebruiken andere scheidingstekens en je zult ze allemaal in het wild tegenkomen:

  • TSV (Tab-Separated Values) — gebruikt \t als scheidingsteken. Veel voorkomend in bioinformatica (BLAST-uitvoer, VCF-bestanden), database-exports en overal waar waarden zelf vaak komma's bevatten
  • Puntkomma-gescheiden — de standaard in Excel voor landinstellingen waar de komma het decimale scheidingsteken is (Duitsland, Frankrijk, het grootste deel van de EU). Als je ooit een CSV van een Europese collega hebt geopend en één gigantische kolom hebt gekregen, is dit de reden
  • Pipe-gescheiden — gebruikt |. Veel voorkomend in verouderde bank- en verzekeringsdataexports waar tabs mogelijk worden verwijderd door mainframesystemen
  • Vaste breedte — technisch gezien geen CSV, maar vaak in dezelfde categorie ingedeeld. Kolommen worden opgevuld tot vaste breedtes in plaats van begrensd

De meeste CSV-parsers laten je het scheidingsteken expliciet opgeven. Wanneer je een CSV schrijft die anderen zullen gebruiken, documenteer je scheidingsteken. Wanneer je er een leest die je niet hebt gemaakt, controleer dan de eerste paar regels voordat je aannames doet. De CSV Formatter kan je helpen bestanden met niet-standaard scheidingstekens te inspecteren en te herformatteren.

CSV Parsen in Python

De standaardbibliotheek van Python bevat de csv-module, en die is genuien goed. De twee klassen die je het meest zult gebruiken zijn csv.reader voor rij-als-lijst-toegang en csv.DictReader voor rij-als-woordenboek-toegang (wat bijna altijd is wat je wilt).

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']}")

Twee dingen die je altijd moet opnemen: encoding='utf-8' (of wat het bestand feitelijk gebruikt), en newline=''. Die tweede is cruciaal — de csv-module doet zijn eigen regeleindebehandeling en heeft de ruwe bytes nodig. Zonder newline='' kun je extra lege rijen krijgen op Windows.

Schrijven is even eenvoudig met csv.writer:

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'])

De csv-module zorgt automatisch voor alle aanhalingstekens — als een veld een komma of een nieuwe regel bevat, zet het die automatisch tussen dubbele aanhalingstekens zonder dat je erover na hoeft te denken. Dat is het hele punt van het gebruik van een bibliotheek.

CSV Parsen in JavaScript / Node.js

JavaScript heeft geen ingebouwde CSV-parser. De verleiding is om dit te doen:

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

Dat mislukt zodra een waarde een komma bevat, een nieuwe regel binnen aanhalingstekens, of een dubbel aanhalingsteken met escape. Gebruik voor alles wat echt is een bibliotheek. PapaParse is de eerste keus — het is snel, verwerkt alle randgevallen, werkt zowel in de browser als in Node.js en ondersteunt streaming voor grote bestanden.

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');

De optie dynamicTyping is handig maar de moeite waard om te kennen — het converteert dingen zoals "34.99" automatisch naar een getal. Dat is meestal wat je wilt, maar het kan je verrassen als een veld zoals order_id een getal is in de CSV maar je het als een string wilde. Schakel het uit als je strikte stringuitvoer nodig hebt.

Voor snelle conversies tussen CSV en andere formaten verwerkt de CSV to JSON-tool de meest voorkomende gevallen in de browser zonder enige code — handig voor eenmalige datatransformaties. Er is ook CSV to XML als je de gegevens aan een XML-gebaseerd systeem moet leveren.

Veelvoorkomende Valkuilen die Je Zeker Tegenkomt

Zelfs met een goede parser heeft CSV een paar bekende landmijnen. Hier zijn de meest voorkomende:

  • BOM-bytes van Excel. Wanneer Excel een CSV exporteert als "UTF-8", voegt het vaak een UTF-8 BOM toe (EF BB BF in hex, of \ufeff als teken). Dit zorgt ervoor dat de eerste kolomheader eruitziet als order_id in plaats van order_id. Open in Python het bestand met encoding='utf-8-sig' in plaats van utf-8 om het automatisch te verwijderen. PapaParse verwerkt het transparant.
  • CRLF vs LF regeleindes. RFC 4180 specificeert CRLF (\r\n), maar Unix-tools produceren LF (\n) en oude Mac-bestanden gebruiken alleen CR (\r). Daarom heeft de Python csv-module newline='' nodig — die verwerkt alle drie intern. Als je ruwe bytes leest en handmatig splitst, moet je \r expliciet verwijderen.
  • Coderingsproblemen. CSV heeft geen manier om zijn eigen codering te declareren — in tegenstelling tot HTML's <meta charset> of XML's <?xml encoding="..."?>. Excel slaat bestanden vaak op in Windows-1252 (ook bekend als CP1252) in plaats van UTF-8, wat tekens met accenten verminkt. Als je tekens zoals é ziet in plaats van é, heb je een UTF-8-bestand dat als Latin-1 wordt gedecodeerd, of vice versa. Stel de codering altijd buiten het systeem vast met wie het bestand produceert.
  • Getallen die eruitzien als datums. Excel converteert stilletjes waarden zoals 1-2 of 03/04 naar datums bij het openen of opslaan. Als je productcodes of versienummers exporteert, zet er een enkel aanhalingsteken voor in Excel ('1-2) om dit te voorkomen — of zeg aan degene die het bestand produceert dat dit moet doen.
  • Achterliggende komma's. Sommige exporteurs geven een achterliggende komma aan het einde van elke regel, waardoor een nep lege kolom ontstaat. Een robuuste parser negeert het; een naïeve split maakt een extra leeg string-element.

Als je met een bestand werkt dat er vreemd uitziet, kan de CSV Validator je snel vertellen of het bestand goed gevormd is en problemen met codering of structuur markeren voordat je het probeert te verwerken.

CSV vs JSON vs Excel — Wanneer Wat te Gebruiken

Deze drie formaten overlappen veel in de praktijk, maar elk heeft een duidelijk sterk punt:

  • Gebruik CSV wanneer je platte, tabulaire gegevens tussen systemen verplaatst — database-exports, analysepijplijnen, spreadsheet-imports, bulk dataladingen. Het wordt universeel ondersteund, is klein van formaat en triviaal te vergelijken in git. De beperking: het is plat. Geen nesting, geen typen, geen relaties.
  • Gebruik JSON wanneer gegevens hiërarchisch zijn of schema's van belang zijn. Een order met meerdere regelitems, een configuratiebestand met geneste objecten, een API-antwoord — dit zijn van nature JSON. CSV zou je dwingen de data te denormaliseren of je eigen nestconventie te verzinnen. De JSON-specificatie is helder en ondubbelzinnig; het formaat behoudt typen (getallen, booleans, null, arrays, objecten).
  • Gebruik Excel (.xlsx) wanneer de uitvoer voor mensen is, niet voor machines. Opmaak, formules, meerdere bladen, grafieken — als een zakelijke gebruiker de eindgebruiker is, is Excel vaak de juiste keuze. Gebruik het nooit als uitwisselingsformaat tussen systemen. De OOXML-specificatie is enorm en het formaat is kwetsbaar over versies heen.

Er is een praktische vuistregel: als je de gegevens van nature in een spreadsheet zou bekijken, is CSV waarschijnlijk goed. Als je ze in een boom of een code-editor zou bekijken, gebruik JSON. Als je ze naar een zakelijke stakeholder stuurt die gaat filteren en sorteren, gebruik Excel.

Samenvatting

CSV is eenvoudig van ontwerp en misleidend lastig in de praktijk. Het formaat heeft geen afgedwongen standaard, komt in meerdere scheidingstekenvarianten en vertrouwt op aanhalingstekensregels die elke handmatige parseerpoging breken. De oplossing is altijd hetzelfde: gebruik een echte parser (de ingebouwde csv-module van Python, of PapaParse in JavaScript), geef de codering altijd expliciet op en let op BOM-bytes van Excel. Zodra je een betrouwbare parser hebt, is CSV eigenlijk een geweldig formaat — snel te produceren, gemakkelijk te inspecteren in elke teksteditor en overal ondersteund. Voor dagelijks werk met CSV-bestanden verwerken de tools CSV to JSON, CSV Formatter en CSV Validator de meest voorkomende bewerkingen zonder een regel code te schrijven.