CSV er et af de formater ingen elsker men alle bruger. Det har eksisteret siden 1970'erne, er stadig standardeksportformatet for enhver regnearkapp på planeten, og sidder næsten helt sikkert et sted i din datapipeline lige nu. Formatet ser trivielt enkelt ud — bare værdier adskilt af kommaer — men at parse det korrekt er overraskende nemt at gøre forkert. Lad os gennemgå hvordan det faktisk fungerer, hvor det bliver rodet, og hvordan man håndterer det i Python og JavaScript uden at skyde sig selv i foden.
Hvad CSV faktisk er
CSV står for Comma-Separated Values. En CSV-fil er ren tekst — ingen binær kodning, ingen metadata, ingen struktur ud over rækker og kolonner. Hver linje er en post, hver værdi inden for en linje er adskilt af en afgrænsning (normalt et komma), og den første række er konventionelt en header-række med kolonnenavne. Her er hvad en rigtig CSV ser ud som:
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,falseDet er alt — seks kolonner, fire datarækker og en header. Ingen vinkelparenteser, ingen krøllede parenteser,
ingen anførselstegn nødvendig (endnu). Denne enkelhed er præcis hvorfor CSV består: næsten ethvert
værktøj på jorden kan åbne, læse og producere det. Excel, Google Sheets, PostgreSQLs COPY-kommando,
pandas, Rs read.csv() — alle CSV out of the box. Afvejningen er at formatet har næsten ingen struktur:
ingen typer, ingen indlejring, inget skema. Enhver værdi er en streng indtil du beslutter andet.
RFC 4180 — Standarden der ikke rigtig håndhæves
Der er en specifikation: RFC 4180, publiceret i 2005. Den definerer ting som CRLF-linjeskift, dobbelt-anførselstegns-escaping og håndtering af indlejrede kommaer. Men her er sagen — RFC 4180 er informationel, ikke en standard. Den beskriver hvad der allerede var almindelig praksis, ikke lovgiver det. Ingen er forpligtet til at følge den, og i praksis gør næsten ingen det præcist.
Resultatet er CSV-dialekt-kaos. Du har filer der bruger LF i stedet for CRLF, filer hvor den første række kan være en header eller ej, filer med et afsluttende linjeskift og filer uden, filer med en UTF-8 BOM tilføjet af Excel. Wikipedia-artiklen om CSV har en solid gennemgang af alt der varierer i praksis. Den sikreste tilgang: antag aldrig noget om en CSV-fil du ikke selv har produceret, og brug altid en ordentlig parser frem for naivt at dele på kommaer.
Citatregler: når kommaer optræder inde i værdier
Det er her "bare del på kommaer" bryder sammen. Hvad sker der når en værdi indeholder et komma? Eller et linjeskift? Eller et dobbelt anførselstegn? RFC 4180 — og de fleste virkelige parsere — håndterer det ved at omgive feltet med dobbelte anførselstegn. Her er det fulde regelsæt:
- Hvis et felt indeholder et komma, et linjeskift eller et dobbelt anførselstegn, omgiv hele feltet med dobbelte anførselstegn
- Hvis et felt indeholder et dobbelt anførselstegn, escape det ved at fordoble det (
"") - Citerede felter kan spænde over flere linjer — linjeskiftet bliver en del af værdien
- Mellemrum inden for anførselstegn er betydningsfuldt og skal bevares
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.50I det eksempel: Davids notes indeholder et komma, så det er citeret. Emmas note indeholder
dobbelte anførselstegn, så de fordobles inden i de ydre anførselstegn. Franks adresse spænder over flere linjer —
linjeskiftene er en del af værdien. Et naivt line.split(',') i ethvert sprog vil komplet ødelægge
alle tre af disse. Det er derfor du har brug for en rigtig parser.
split kan gøre korrekt.Afgrænsningsvarianter: tabulatorer, semikoloner, pipes
På trods af navnet behøver CSV ikke bruge kommaer. Flere almindelige varianter bruger forskellige afgrænsere, og du vil støde på dem alle i virkeligheden:
- TSV (Tab-Separated Values) — bruger
\tsom afgrænsning. Almindelig inden for bioinformatik (BLAST-output, VCF-filer), databaseeksporter og overalt hvor værdier ofte indeholder kommaer - Semikolonsepareret — standard i Excel for lokaliteter hvor kommaet er decimalseparatoren (Tyskland, Frankrig, det meste af EU). Hvis du nogensinde har åbnet en CSV fra en europæisk kollega og fået én kæmpe kolonne, er det derfor
- Pipesepareret — bruger
|. Almindelig i ældre bank- og forsikringsdataeksporter hvor tabulatorer kan fjernes af mainframe-systemer - Fast bredde — teknisk set ikke CSV, men ofte i samme kategori. Kolonner er polstret til faste bredder frem for afgrænset
De fleste CSV-parsere lader dig angive afgrænsningen eksplicit. Når du skriver en CSV som andre vil forbruge, dokumenter din afgrænsning. Når du læser en du ikke producerede, kontroller de første par linjer inden du antager. CSV Formatter-værktøjet kan hjælpe dig med at inspicere og reformatere filer med ikke-standardafgrænsninger.
Parsing af CSV i Python
Pythons standardbibliotek inkluderer
modulen csv,
og den er genuint god. De to klasser du vil bruge mest er csv.reader for række-som-liste-adgang
og csv.DictReader for række-som-dict-adgang (hvilket næsten altid er hvad du vil have).
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']}")To ting der altid skal inkluderes: encoding='utf-8' (eller hvad filen faktisk bruger)
og newline=''. Det andet er kritisk — modulen csv håndterer sine egne linjeskift
og har brug for råa bytes. Uden newline='' kan du få ekstra tomme rækker på Windows.
Skrivning er ligeså ligetil med csv.writer:
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'])Modulen csv tager sig af al citering automatisk — hvis et felt indeholder et komma
eller et linjeskift, omgiver den det med dobbelte anførselstegn uden at du behøver tænke på det. Det er hele
pointen med at bruge et bibliotek.
Parsing af CSV i JavaScript / Node.js
JavaScript har ingen indbygget CSV-parser. Fristelsen er at gøre dette:
// ❌ Don't do this — breaks immediately on quoted fields
const rows = csvText.split('\n').map(line => line.split(','));Det fejler øjeblikkeligt når en værdi indeholder et komma, et linjeskift inden for anførselstegn eller et escaped dobbelt anførselstegn. Til noget reelt skal du bruge et bibliotek. PapaParse er det foretrukne valg — det er hurtigt, håndterer alle kanttilfælde, fungerer i både browser og Node.js og understøtter streaming til store filer.
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');Indstillingen dynamicTyping er praktisk men værd at kende til — den konverterer automatisk ting som
"34.99" til et tal. Det er normalt hvad du vil have, men det kan overraske dig hvis
et felt som order_id tilfældigvis er et tal i CSV'en men du ville have det som en streng.
Sluk for det hvis du har brug for strengt strengoutput.
Til hurtige konverteringer mellem CSV og andre formater håndterer CSV til JSON-værktøjet de fleste almindelige tilfælde i browseren uden nogen kode — nyttigt til engangsdatatransformationer. Der er også CSV til XML hvis du skal levere data til et XML-baseret system.
Almindelige faldgruber du bestemt vil støde på
Selv med en god parser har CSV et par velkendte miner. Her er dem der dukker op oftest:
- BOM-bytes fra Excel. Når Excel eksporterer en CSV som "UTF-8" tilføjer den ofte en
UTF-8 BOM (
EF BB BFi hex, eller\ufeffsom et tegn). Dette får den første kolonneoverskrift til at se ud somorder_idi stedet fororder_id. I Python, åbn filen medencoding='utf-8-sig'i stedet forutf-8for at fjerne den automatisk. PapaParse håndterer det transparent. - CRLF vs LF-linjeskift. RFC 4180 specificerer CRLF (
\r\n), men Unix-værktøjer producerer LF (\n) og gamle Mac-filer bruger CR (\r) alene. Det er derfor Pythonscsv-modul har brug fornewline=''— den håndterer alle tre internt. Hvis du læser rå bytes og deler manuelt, skal du eksplicit fjerne\r. - Kodningsproblemer. CSV har ingen måde at deklarere sin egen kodning på — i modsætning til
HTMLs
<meta charset>eller XMLs<?xml encoding="..."?>. Excel gemmer ofte filer i Windows-1252 (aka CP1252) frem for UTF-8, hvilket ødelægger accentuerede tegn. Hvis du ser tegn soméi stedet foré, har du en UTF-8-fil der afkodes som Latin-1, eller omvendt. Etabler altid kodning ud-af-båndet med den der producerer filen. - Tal der ligner datoer. Excel konverterer stille værdier som
1-2eller03/04til datoer ved åbning eller lagring. Hvis du eksporterer produktkoder eller versionsnumre, foranstil dem med et enkelt anførselstegn i Excel ('1-2) for at forhindre dette — eller bed den der producerer filen om at gøre det. - Afsluttende kommaer. Nogle eksportører udsender et afsluttende komma i slutningen af hver linje, hvilket skaber en fantom-tom kolonne. En robust parser ignorerer det; et naivt split skaber et ekstra tomt strengelement.
Hvis du arbejder med en fil der ser mærkelig ud, kan CSV Validator-værktøjet hurtigt fortælle dig om filen er velformet og markere kodnings- eller strukturelle problemer inden du forsøger at behandle den.
CSV vs JSON vs Excel — hvornår man bruger hvad
Disse tre formater overlapper meget i praksis, men hvert har et klart kernekompetenceområde:
- Brug CSV når du flytter flade, tabelformede data mellem systemer — databaseeksporter, analysepipelines, regnearksimporter, massedatalastning. Det understøttes universelt, er lille og trivielt diff-bart i git. Begrænsningen: det er fladt. Ingen indlejring, ingen typer, ingen relationer.
- Brug JSON når data er hierarkisk eller skema har betydning. En ordre med flere linjer, en konfigurationsfil med indlejrede objekter, et API-svar — disse er naturligt JSON. CSV ville tvinge dig til at denormalisere data eller opfinde din egen indlejringskonvention. JSON-specifikationen er ren og entydig; formatet bevarer typer (tal, booleaner, null, arrays, objekter).
- Brug Excel (.xlsx) når outputtet er til mennesker, ikke maskiner. Formatering, formler, flere ark, diagrammer — hvis en erhvervsbruger er den endelige forbruger er Excel ofte det rigtige valg. Brug det aldrig som et udvekslingsformat mellem systemer. OOXML-specifikationen er enorm og formatet er skrøbeligt på tværs af versioner.
Der er en praktisk heuristik: hvis du naturligt ville se data i et regneark er CSV sandsynligvis fint. Hvis du ville se det i et træ eller en kodeeditor, brug JSON. Hvis du sender det til en forretningsinteressent der vil filtrere og sortere det, brug Excel.
Opsummering
CSV er enkelt af design og vildledende tricky i praksis. Formatet har ingen håndhævet standard,
leveres i flere afgrænsningsvarianter og er afhængigt af citatregler der bryder ethvert manuelt parseringsforsøg.
Løsningen er altid den samme: brug en rigtig parser (Pythons indbyggede csv-modul eller PapaParse
i JavaScript), angiv altid kodningen eksplicit, og pas på BOM-bytes fra Excel. Når du har en
pålidelig parser på plads er CSV faktisk et fremragende format — hurtig at producere, let at inspicere i enhver
teksteditor og understøttet overalt. Til dagligt arbejde med CSV-filer håndterer værktøjerne
CSV til JSON, CSV Formatter og
CSV Validator de almindelige operationer uden at skrive en linje kode.