Zwischen Listenformaten konvertieren, ohne Daten zu verlieren: die Anführungsregeln, die niemand liest
Veröffentlicht am 30.6.2025 · 12 Min. Lesezeit · Text- & Sprach-Tools
Daniel Okonkwo — Front-end-Entwickler und Tech-Redakteur bei Allin
Web-Performance · Dateiformate
Anhand von 6 Quellen geprüft
Aus einer Liste mit Zeilenumbrüchen eine Kommaliste zu machen ist eine Codezeile, bis ein Eintrag ein Komma enthält. Drei Namen — Müller, Anna / Schmidt, Jan / O’Brien, Tim — mit Kommas verbunden und wieder zerlegt ergeben sechs Einträge, nicht drei. Die Abhilfe ist eine Anführungsregel, und zu befolgen ist RFC 4180: Ein Feld, das ein Komma, ein doppeltes Anführungszeichen oder einen Zeilenumbruch enthält, muss in doppelte Anführungszeichen gesetzt werden, und ein doppeltes Anführungszeichen im Inneren wird durch Verdoppeln maskiert. So geschrieben, liest sich "Müller, Anna","Schmidt, Jan","O’Brien, Tim" wieder als genau drei Einträge. Zwei Folgen überraschen. Ein CSV-Feld darf legal einen Zeilenumbruch enthalten: Eine Datei mit zwei Datensätzen kann drei physische Zeilen belegen, und ein Zerlegen am Zeilenendezeichen ist schlicht falsch — Datensätze werden durch CRLF getrennt, und nur ein echter Parser weiß, welche Umbrüche zählen. Und eine leere Zeichenkette ist nicht null Einträge: Zerlegt man die leere Zeichenkette am Komma, kommt ein leerer Eintrag heraus, während das Verbinden von [] und von [""] beide die leere Zeichenkette liefern — beide Listen werden also ununterscheidbar, wenn nicht jedes Feld in Anführungszeichen steht. Konvertiere nie mit einem naiven Zerlegen am Trennzeichen.
Aus einer Liste mit Zeilenumbrüchen eine Kommaliste zu machen ist trivial, bis ein Eintrag ein Komma enthält. Die Anführungsregeln von RFC 4180, warum ein CSV-Feld einen Zeilenumbruch enthalten darf, warum europäische Tabellenkalkulationen das Semikolon verwenden und was ein leerer Eintrag mit dem Hin- und Rückweg macht — jeder Fall ausgeführt und abgedruckt.
Die eine Codezeile und der genaue Moment, in dem sie bricht
Zeilenliste zu Kommaliste ist ein Verbinden. Umgekehrt ist es ein Zerlegen. Beides ist genau so lange korrekt, wie kein Eintrag das Trennzeichen enthält, und sobald einer es enthält, hört die Konvertierung auf, umkehrbar zu sein, ohne das zu sagen. Unsere Testliste aus fünf Einträgen — Smith, John / Doe, Jane / O’Neill, „Bud“ / eine zweizeilige Adresse / plain — mit Kommas verbunden und wieder zerlegt ergab acht Einträge. Nichts warf eine Ausnahme, nichts warnte, und drei der acht waren Namensfragmente. Diese Stille ist das ganze Problem: Ein Listenkonverter, der Daten verliert, erzeugt eine plausible Liste, keinen Fehler.
Dasselbe Experiment in jeder unserer Sprachen ergibt dieselbe Form: Drei Einträge „Nachname, Vorname“ werden nach einem naiven Hin- und Rückweg zu sechs, auf Englisch, Französisch, Spanisch, Portugiesisch, Deutsch und Italienisch gleichermaßen. Durch einen RFC-4180-Schreiber geschrieben und mit einem RFC-4180-Parser zurückgelesen, kommen alle als drei Einträge zurück, identisch zur Eingabe. Die Regel ist nicht sprachspezifisch; nur die Daten, die über sie stolpern, sind es.
Was RFC 4180 tatsächlich sagt
RFC 4180 ist kurz, vom Oktober 2005 und — wissenswert — Informational statt Standard. Deine sieben Regeln lauten: Datensätze werden durch CRLF getrennt; der letzte muss nicht damit enden; eine optionale Kopfzeile darf vorangehen; Felder werden durch Kommas getrennt und Leerzeichen gehören zum Feld; Anführungszeichen sind optional, aber ein nicht zitiertes Feld darf kein doppeltes Anführungszeichen enthalten; Felder mit Zeilenumbrüchen, doppelten Anführungszeichen oder Kommas sollen in doppelte Anführungszeichen gesetzt werden; und ein doppeltes Anführungszeichen im Inneren wird maskiert, indem ihm ein weiteres vorangestellt wird. Genau für die letzte Regel erfindet man gern eine Alternative, meist einen Rückstrich, den kein CSV-Leser erwartet.
Die ABNF-Grammatik in Abschnitt 2 ist strenger als alles in der Praxis. TEXTDATA ist als %x20-21 / %x23-2B / %x2D-7E definiert, was das Komma bei %x2C und das doppelte Anführungszeichen bei %x22 ausschließt — und jedes Byte über 127 ebenfalls. Wir haben geprüft: „plain“ ist TEXTDATA-konform; „café“, „naïve“, „Straße“ und „ação“ sind es nicht. In der Praxis trägt der charset-Parameter des Medientyps text/csv die Kodierung, und alle schreiben ohnehin UTF-8, aber es erinnert daran, dass die RFC ein bestehendes Durcheinander kodifiziert und kein Format entworfen hat. Das Dokument sagt es selbst und empfiehlt, konservativ zu erzeugen und liberal zu akzeptieren.
Ein CSV-Feld darf einen Zeilenumbruch enthalten
Das ist die Regel, an der die meisten Importeure scheitern, weil sie dem Denkmodell eines Datensatzes pro Zeile widerspricht. Wir haben eine Datei mit zwei Datensätzen geschrieben, deren zweites Feld eine zweizeilige Adresse ist: Die Bytes lauten id-1,"Line one CRLF Line two",ok CRLF id-2,flat,ok. Am Zeilenumbruch zerlegt ergibt das drei Fragmente; korrekt geparst zwei Datensätze, deren erster den Zeilenumbruch unversehrt enthält. Jeder Code, der eine CSV-Datei mit readLines liest, ist bei dieser Eingabe falsch, und die Eingabe ist nicht exotisch — Adressen, Produktbeschreibungen und eingefügte Notizen enthalten Zeilenumbrüche.
Zeilenenden verdienen einen eigenen Absatz. Die RFC verlangt CRLF zwischen Datensätzen, und wenn man a,b CRLF c,d CRLF nur am Zeilenvorschub zerlegt, erhält man ["a,b\r", "c,d\r", ""] — zwei Felder mit angeklebtem unsichtbarem Wagenrücklauf und ein leeres Ende. Dieses verirrte \r ist der Grund, warum ein Wert zwischen zwei Systemen ungleich sich selbst ist, und warum eine abschließende leere Zeichenkette zur Geisterzeile wird. Ein Parser, der CRLF, LF und ein einzelnes CR als Datensatztrenner verarbeitet, bewältigt alle drei Dateifamilien und liefert für jede dieselben zwei Datensätze.
Warum halb Europa CSV mit Semikolons schreibt
Fragt man die Plattform, wie eine Zahl in jeder unserer sechs Locales aussieht, ist der Zusammenstoß offensichtlich. Formatiert man 1234567.5, ergibt das 1,234,567.5 in en-US, 1 234 567,5 in fr-FR mit schmalem geschütztem Leerzeichen U+202F als Gruppentrenner, 1.234.567,5 in es-ES, de-DE und it-IT sowie 1 234 567,5 in pt-PT mit U+00A0. Fünf der sechs verwenden das Komma als Dezimalzeichen. Eine kommagetrennte Preisliste in diesen Locales enthält daher in jeder Zeile ein Komma innerhalb eines Feldes — und genau deshalb schreiben und erwarten ihre Tabellenprogramme stattdessen das Semikolon.
Der Lauf macht es konkret. Die Zeile Stuhl / 1.299,00 / 2 mit Kommas verbunden wird als vier Felder gelesen — Stuhl, 1.299, 00, 2 —, weil das Dezimalzeichen im Deutschen ein Komma ist. Die Zeile Chair / 1,299.00 / 2 wird aus dem spiegelbildlichen Grund ebenfalls als vier gelesen: Der Tausendertrenner ist im Englischen ein Komma. Setzt man das Preisfeld in Anführungszeichen, sind beide wieder drei Felder; nimmt man ein Semikolon, sind beide drei Felder ganz ohne Anführungszeichen. Keiner der Wege ist richtiger: Das Semikolon öffnet eine europäische Tabellenkalkulation ohne Importdialog, zitierte Kommas nimmt eine API an.
Leere Einträge, abschließende Trennzeichen und was kein Hin- und Rückweg rettet
Zerlegt man die leere Zeichenkette am Komma, kommt ein leerer Eintrag heraus, nicht null. „a,b,“ ergibt drei Einträge, der letzte leer. „,a“ ergibt zwei, der erste leer. „a,,b“ ergibt drei, der mittlere leer. Keines davon ist ein Fehler; alle folgen aus einer Definition — ein Trenner trennt, also bedeuten n Trenner n+1 Einträge. Gewollt ist meist die gefilterte Fassung, und Filtern ist eine Entscheidung, die ein wirklich leeres Feld stillschweigend löscht.
Der unrettbare Fall liegt am Schreibende. Das Verbinden der leeren Liste und das Verbinden einer Liste mit einer leeren Zeichenkette ergeben beide die leere Zeichenkette; beide sind auf der Leitung identisch, und kein Parser kann sie unterscheiden. Unser eigener minimaler Parser machte es schlimmer: Beim Lesen der leeren Zeichenkette lieferte er null Datensätze — richtig für die eine Eingabe, falsch für die andere. Ein Schreiber, der jedes Feld in Anführungszeichen setzt, behebt genau das: Ein leerer Eintrag wird zu den zwei Zeichen "" und liest sich als ein einzelner leerer Eintrag zurück, während null Einträge die leere Zeichenkette bleiben und sich als nichts zurücklesen. Wenn leere Einträge in deinen Daten vorkommen können, ist Immer-Zitieren keine Stilfrage.
JSON, Tabulatoren und ein Trennzeichen mit Absicht wählen
Ein JSON-Array umgeht die ganze Diskussion, indem es alles zitiert und den Rest maskiert: Unsere Liste aus fünf Einträgen überstand den Hin- und Rückweg unverändert, die zweizeilige Adresse als eine einzige Zeichenkette mit \n gespeichert. Das ist das Format der Wahl, wenn an beiden Enden ein Programm steht. Der Preis: Jeder Konsument muss ein JSON-Parser sein, und JSON hat Typen — eine Liste von Postleitzahlen kommt als Zahlen zurück, wenn jemand sie ohne Anführungszeichen schreibt, und 01234 kommt als 1234 oder als Syntaxfehler zurück.
Tabulatorgetrennte Werte sind das Format ohne Spezifikation, weshalb sie so oft funktionieren und so leise scheitern. Unsere gegnerische Liste enthielt einen Tabulator innerhalb eines Eintrags, ein Tabulator-Trennzeichen hätte ihn also zerschnitten. Schau hin, bevor du ein Trennzeichen wählst: In dieser Liste kamen Komma, Semikolon und Tabulator innerhalb von Einträgen vor, der senkrechte Strich, U+001F und das Nullbyte nicht. Ein Trennzeichen, dessen Abwesenheit belegt ist, macht aus der Konvertierung wieder die eine Codezeile, nach der sie aussah — und wenn keines sicher ist, setzt du Anführungszeichen. Eine letzte praktische Anmerkung ohne Bezug zum Parsen: Ein Feld, das mit =, +, - oder @ beginnt, wird von Tabellenprogrammen als Formel behandelt; bei einer Liste benutzergelieferter Zeichenketten sollten solche Felder entschärft werden, bevor jemand die Datei öffnet.
| Format | Eintragstrenner | Trenner innerhalb eines Eintrags | Zeilenumbruch innerhalb eines Eintrags | Einträge nach Hin- und Rückweg |
|---|---|---|---|---|
| Ein Eintrag pro Zeile | Zeilenvorschub | Kein Problem — Kommas sind gewöhnliche Zeichen | Unmöglich — er beendet den Eintrag | 3 von 3 |
| Naives Verbinden mit Kommas | Komma, ohne Anführungszeichen | Bricht — der Eintrag zerfällt in zwei | Bricht — sieht aus wie ein neuer Datensatz | 6 von 3 |
| RFC-4180-CSV | Komma, Felder bei Bedarf in Anführungszeichen | Feld in Anführungszeichen setzen | Legal in einem zitierten Feld | 3 von 3 |
| Semikolon-CSV (europäische Tabellenkalkulationen) | Semikolon | Gleiche Anführungsregel, anderes Trennzeichen | Legal in einem zitierten Feld | 3 von 3 |
| JSON-Array | Komma zwischen zitierten Zeichenketten | Kein Problem — jede Zeichenkette ist zitiert | Als \n maskiert | 3 von 3 |
| Tabulatorgetrennt | Tabulator | Nur sicher, wenn kein Eintrag einen Tabulator enthält — unserer tat es | Von keiner Norm definiert | 3 von 3 nur mit Glück |
Häufige Fragen
- Kann ein CSV-Feld wirklich einen Zeilenumbruch enthalten?
- Ja, und RFC 4180 sagt es ausdrücklich: Ein Feld mit Zeilenumbrüchen soll in doppelte Anführungszeichen gesetzt werden, und die ABNF erlaubt CR und LF innerhalb eines maskierten Feldes. Wir haben eine Datei mit zwei Datensätzen geschrieben, deren zweites Feld eine zweizeilige Adresse enthielt; sie belegt drei physische Zeilen, Zeilenzählen ergibt also 3 und Parsen 2. Jeder Importeur, der auf Zeilenlesen beruht, ist bei dieser Datei falsch.
- Ist eine semikolongetrennte Datei noch CSV?
- Nach RFC 4180 nicht, deren ABNF das Trennzeichen auf %x2C, das Komma, festlegt. In der Praxis ist es das, was Tabellenprogramme in jeder Locale mit Dezimalkomma schreiben — fünf unserer sechs. Behalte die übrigen Regeln bei: Felder mit dem Trennzeichen zitieren, Anführungszeichen verdoppeln, Datensätze mit CRLF trennen. Benenn die Datei ehrlich und nenn das Trennzeichen bei der Übergabe.
- Ist eine leere Zeichenkette ein Eintrag oder keiner?
- Das Zerlegen sagt einer: Zerlegt man die leere Zeichenkette am Komma, kommt eine Liste mit einem leeren Eintrag heraus. Das Verbinden kann es nicht sagen, denn [] und [""] liefern beide die leere Zeichenkette. Die Antwort ist also eine Konvention, die du wählen und festhalten musst, nichts, was in den Daten steckt. Der einzige Weg, die Unterscheidung über einen Hin- und Rückweg zu retten, ist jedes Feld zu zitieren: Ein leerer Eintrag wird zu zwei Anführungszeichen, null Einträge zu gar nichts.
- Wie maskiere ich ein doppeltes Anführungszeichen in einem Feld?
- Indem du es verdoppelst, innerhalb eines zitierten Feldes. Der Eintrag say "hi" wird als "say ""hi""" geschrieben — ein öffnendes Anführungszeichen, der Text mit jedem inneren Anführungszeichen doppelt, ein schließendes. Ein Rückstrich bewirkt nichts; CSV kennt keine Rückstrich-Maskierung. Unsere gegnerische Liste aus elf Einträgen, die diese Zeichenkette und eine bereits zitierte enthielt, lief unter dieser Regel identisch hin und zurück.
- Wann sollte ich stattdessen ein JSON-Array nehmen?
- Immer wenn an beiden Enden Programme stehen. JSON zitiert jede Zeichenkette und maskiert Steuerzeichen, also brauchen Einträge mit Kommas, Anführungszeichen und Zeilenumbrüchen keine Sonderbehandlung: Unsere Liste aus fünf Einträgen lief unverändert hin und zurück, der zweizeilige Eintrag mit \n in einer einzigen Zeichenkette gespeichert. Nimm CSV, wenn am anderen Ende eine Tabellenkalkulation oder ein Mensch sitzt, und denk daran, dass JSON Typen hat: Bezeichner mit führender Null müssen Zeichenketten bleiben.
Artikel, die dich interessieren könnten
Alle Ratgeber →Ähnliche Tools
Quellen
- IETF (RFC Editor) — RFC 4180 — Common Format and MIME Type for Comma-Separated Values (CSV) Files, October 2005, Informational
- IANA — Media type registration for text/csv, with the optional charset and header parameters
- W3C — Model for Tabular Data and Metadata on the Web — what a CSV file does and does not carry
- Ecma International — ECMA-404 — The JSON Data Interchange Syntax
- Unicode Consortium (CLDR) — Common Locale Data Repository — per-locale decimal and grouping separators
- OWASP — CSV Injection — why a field beginning with =, +, - or @ is a security concern in spreadsheets
Hast du einen Fehler in diesem Artikel entdeckt?