Zum Inhalt

CSV-Dateien

CSV ist eine einfache Tabelle als Textdatei: eine Zeile pro Datensatz, die Werte durch Komma oder Semikolon getrennt. Excel und fast jedes andere Programm kann CSV lesen und schreiben — deshalb ist es das Standardformat für Import und Export.

Name;Ort;Umsatz
Bäckerei Schmitz;Köln;12500,00
Autohaus Weber;Berlin;98000,50

Ausführliches dazu: CSV schreiben & einlesen. Die Beispiele speichern Dateien im Übungsordner /beispiele/csv der Datei-Sandbox; das letzte Beispiel räumt ihn wieder auf.

Einsteiger

1. Einen CSV-Text lesen

csv.parse zerlegt einen Text in Zeilen und Werte. Heraus kommt eine Liste von Zeilen, jede Zeile ist eine Liste von Werten.

var zeilen = csv.parse("Schmitz,Köln\nWeber,Berlin");
log.info(zeilen.length + " Zeilen");
log.info("Zeile 1, Wert 2: " + zeilen[0][1]);   // Köln

Die Zählung beginnt bei 0: zeilen[0] ist die erste Zeile.

2. Die Kopfzeile nutzen

Mit header: true wird die erste Zeile zu Spaltennamen. Jede Zeile ist dann ein Objekt — viel lesbarer.

var text = "Name,Ort\nSchmitz,Köln\nWeber,Berlin";
var leute = csv.parse(text, { header: true });
leute.forEach(function (p) {
  log.info(p.Name + " wohnt in " + p.Ort);
});

3. Semikolon als Trennzeichen

Deutsche Excel-Dateien trennen mit ;. Das stellst du mit separator ein.

var text = "Name;Umsatz\nSchmitz;12500,00";
var zeilen = csv.parse(text, { header: true, separator: ";" });
log.info(zeilen[0].Name + ": " + zeilen[0].Umsatz);

4. Eine CSV erzeugen

Der StandardPrinter baut eine CSV Zeile für Zeile. println([...]) schreibt eine Zeile.

var p = new csv.StandardPrinter({ lineSeparator: "UNIX" });
p.println(["Nr", "Artikel", "Preis"]);
p.println([1, "Bleistift", 0.80]);
p.println([2, "Radiergummi", 1.20]);
log.info(p.toString());

5. Die CSV als Datei speichern

Eine Zuweisung an einen Dateipfad genügt.

var p = new csv.StandardPrinter({ separator: ";" });
p.println(["Name", "Ort", "Umsatz"]);
p.println(["Bäckerei Schmitz", "Köln", "12500,00"]);
p.println(["Autohaus Weber", "Berlin", "98000,50"]);
p.println(["Blumen Meyer", "Köln", "3400,00"]);
fs["/beispiele/csv/kunden.csv"] = p;
log.info("Datei gespeichert: " + fs["/beispiele/csv/kunden.csv"].size + " Byte");

6. Eine CSV-Datei einlesen

csv.parse nimmt statt eines Textes auch direkt eine Datei.

var kunden = csv.parse(fs["/beispiele/csv/kunden.csv"], { header: true, separator: ";" });
log.info(kunden.length + " Kunden gelesen");
log.info("Erster: " + kunden[0].Name);

7. Zahlen aus einer CSV addieren

Werte aus einer CSV sind immer Text. kc.num versteht auch 12500,00 und 1.234,56.

var kunden = csv.parse(fs["/beispiele/csv/kunden.csv"], { header: true, separator: ";" });
var summe = 0;
kunden.forEach(function (k) { summe += kc.num(k.Umsatz); });
log.info("Summe: " + summe.toFixed(2));   // 113900.50

8. Nur bestimmte Zeilen verwenden

filter behält nur die Zeilen, für die die Bedingung stimmt.

var kunden = csv.parse(fs["/beispiele/csv/kunden.csv"], { header: true, separator: ";" });
var koeln = kunden.filter(function (k) { return k.Ort === "Köln"; });
log.info(koeln.length + " Kunden in Köln");

Fortgeschritten

9. Leerzeichen um die Werte entfernen

Manche Programme schreiben "Köln " mit Leerzeichen. trim: true räumt das beim Lesen auf.

var zeilen = csv.parse("Name , Ort\n Schmitz , Köln ", { header: true, trim: true });
log.info("[" + zeilen[0].Ort + "]");   // [Köln]

10. Vorspann-Zeilen überspringen

Exporte haben oft ein paar Zeilen Beschreibung vor der eigentlichen Tabelle. skip überspringt sie.

var text = "Export vom 01.03.2026\nSystem: Warenwirtschaft\nName;Ort\nSchmitz;Köln";
var zeilen = csv.parse(text, { header: true, separator: ";", skip: 2 });
log.info(zeilen[0].Name + " / " + zeilen[0].Ort);

11. Sonderzeichen in Werten

Enthält ein Wert das Trennzeichen, Anführungszeichen oder einen Zeilenumbruch, setzt der Printer ihn automatisch in Anführungszeichen — und der Parser liest ihn wieder richtig.

var p = new csv.StandardPrinter({ separator: ";", lineSeparator: "UNIX" });
p.println(["Firma", "Hinweis"]);
p.println(["Müller; Söhne", 'Sagt "Hallo"']);
log.info(p.toString());   // "Müller; Söhne";"Sagt ""Hallo"""

var zurueck = csv.parse(p.toString(), { header: true, separator: ";" });
log.info("zurückgelesen: " + zurueck[0].Firma + " | " + zurueck[0].Hinweis);

12. Zeile für Zeile lesen

Für sehr große Dateien liest der StandardParser eine Zeile nach der anderen. line ist die Nummer der aktuellen Zeile.

var parser = new csv.StandardParser(fs["/beispiele/csv/kunden.csv"], { separator: ";" });
var kopf = parser.nextLine();   // erste Zeile = Spaltennamen
var zeile;
while ((zeile = parser.nextLine()) !== null) {
  log.info("Zeile " + parser.line + ": " + zeile[0]);
}
parser.close();

nextLine() liefert null, wenn die Datei zu Ende ist.

13. Eine CSV, die Excel richtig öffnet

Deutsches Excel erwartet Semikolon, Windows-Zeilenenden und ein BOM (ein unsichtbares Zeichen am Anfang), damit Umlaute stimmen.

var p = new csv.StandardPrinter({ separator: ";", lineSeparator: "WINDOWS", bom: true });
p.println(["Straße", "Größe"]);
p.println(["Hauptstraße 1", "groß"]);
fs["/beispiele/csv/excel.csv"] = p;

var bytes = fs["/beispiele/csv/excel.csv"].readBytes();
log.info("BOM: " + (bytes[0] === -17 ? "ja" : "nein"));   // 0xEF als Byte = -17

14. An eine vorhandene Datei anhängen

appendTo schreibt ans Ende, statt die Datei zu überschreiben — praktisch für ein Protokoll.

var protokoll = fs["/beispiele/csv/protokoll.csv"];
if (!protokoll.exists) {
  var kopf = new csv.StandardPrinter({ separator: ";" });
  kopf.println(["Zeitpunkt", "Benutzer", "Aktion"]);
  kopf.writeTo(protokoll);
}
var p = new csv.StandardPrinter({ separator: ";" });
p.println([new Date().format("dd.MM.yyyy HH:mm"), ctx.user.name, "Beispiel ausgeführt"]);
p.appendTo(protokoll);

log.info(csv.parse(protokoll, { separator: ";" }).length + " Zeilen im Protokoll");

15. Einen anderen Zeichensatz verwenden

Ältere Programme erwarten ISO-8859-1 statt UTF-8. Beim Schreiben und Lesen gibst du ihn mit an.

var p = new csv.StandardPrinter({ separator: ";", encoding: "ISO-8859-1" });
p.println(["Größe", "Übergröße"]);
fs["/beispiele/csv/alt.csv"] = p;

var zeilen = csv.parse(fs["/beispiele/csv/alt.csv"], { separator: ";", encoding: "ISO-8859-1" });
log.info(zeilen[0][0]);   // Größe – richtig gelesen

Liest du so eine Datei ohne encoding, erscheinen statt Umlauten Fragezeichen.

16. Datenbank-Abfrage als CSV

Eine SQL-Abfrage direkt in eine Datei schreiben.

var p = new csv.StandardPrinter({ separator: ";", bom: true });
p.println(["Login", "Vorname", "Nachname", "E-Mail"]);
var n = 0;
db.forEach("SELECT LOGIN_NAME, FIRST_NAME, LAST_NAME, EMAIL FROM USERS ORDER BY LOGIN_NAME", function (u) {
  p.println([u.login_name, u.first_name, u.last_name, u.email]);
  n++;
});
fs["/beispiele/csv/benutzer.csv"] = p;
log.info(n + " Benutzer exportiert");

17. Eine CSV in eine Detail-Tabelle übernehmen

Jede CSV-Zeile wird zu einer Tabellenzeile im Formular.

var kunden = csv.parse(fs["/beispiele/csv/kunden.csv"], { header: true, separator: ";" });
ctx.form.positionen = kunden.map(function (k) {
  return { name: k.Name, ort: k.Ort, umsatz: kc.num(k.Umsatz) };
});
log.info(ctx.form.positionen.length + " Zeilen in die Tabelle übernommen");

Die Namen links (name, ort, umsatz) müssen die Spaltennamen deiner Detail-Tabelle sein.

18. Nur bestimmte Spalten übernehmen

Aus einer breiten CSV nur zwei Spalten in einer neuen Reihenfolge ausgeben.

var kunden = csv.parse(fs["/beispiele/csv/kunden.csv"], { header: true, separator: ";" });
var p = new csv.StandardPrinter({ separator: ";", lineSeparator: "UNIX" });
p.println(["Ort", "Name"]);
kunden.forEach(function (k) { p.println([k.Ort, k.Name]); });
log.info(p.toString());

Profi

19. Import mit Prüfung

Fehlerhafte Zeilen werden nicht übernommen, sondern mit Zeilennummer gesammelt. So weiß die Fachabteilung genau, was sie korrigieren muss.

var text = "Nr;Name;Menge\n1;Stift;10\n2;;5\n3;Block;viele\n4;Mappe;2";
var zeilen = csv.parse(text, { header: true, separator: ";", trim: true });

var gueltig = [], fehler = [];
// Indizierte Schleife, nicht forEach: auf dem Ergebnis von csv.parse ist
// forEach Javas Fassung und reicht KEINEN Index durch (siehe Hinweis unten).
for (var i = 0; i < zeilen.length; i++) {
  var z = zeilen[i];
  var zeilennr = i + 2;   // +1 wegen Kopfzeile, +1 weil Menschen ab 1 zählen
  if (!z.Name) {
    fehler.push("Zeile " + zeilennr + ": Name fehlt");
  } else if (!/^\d+$/.test(z.Menge)) {
    fehler.push("Zeile " + zeilennr + ": Menge '" + z.Menge + "' ist keine Zahl");
  } else {
    gueltig.push({ nr: z.Nr, name: z.Name, menge: Number(z.Menge) });
  }
}

log.info(gueltig.length + " gültig, " + fehler.length + " fehlerhaft");
fehler.forEach(function (f) { log.warn(f); });

20. CSV in die Datenbank importieren

Erst prüfen, dann in einem Rutsch mit db.batch einfügen. Die Übungstabelle wird am Ende wieder entfernt.

db.update("CREATE TABLE IF NOT EXISTS bsp_import (name VARCHAR(100), ort VARCHAR(60), umsatz DECIMAL(12,2))");

var kunden = csv.parse(fs["/beispiele/csv/kunden.csv"], { header: true, separator: ";" });
var zeilen = kunden.map(function (k) {
  return { name: k.Name, ort: k.Ort, umsatz: kc.num(k.Umsatz) };
});
db.batch("INSERT INTO bsp_import (name, ort, umsatz) VALUES (${name}, ${ort}, ${umsatz})", zeilen);

log.info("importiert: " + db.scalar("SELECT COUNT(*) FROM bsp_import"));
db.update("DROP TABLE bsp_import");   // Übung aufräumen

21. Die CSV zum Herunterladen anbieten

delivery.deliver macht aus den Bytes einen Download-Link, view.Link zeigt ihn im Formular.

var p = new csv.StandardPrinter({ separator: ";", bom: true });
p.println(["Datum", "Wert"]);
p.println([new Date().format("dd.MM.yyyy"), 42]);

var url = delivery.deliver(p.toByteArray(), { name: "auswertung.csv", mime: "text/csv" });
view.add(new view.Link(url, "Auswertung als CSV herunterladen"));
log.info("Link erzeugt: " + url);

22. Gruppensummen berechnen und als Bericht schreiben

Umsätze je Ort addieren und sortiert als neue CSV ausgeben.

var kunden = csv.parse(fs["/beispiele/csv/kunden.csv"], { header: true, separator: ";" });

var jeOrt = {};
kunden.forEach(function (k) {
  jeOrt[k.Ort] = (jeOrt[k.Ort] || 0) + kc.num(k.Umsatz);
});

var p = new csv.StandardPrinter({ separator: ";", lineSeparator: "UNIX" });
p.println(["Ort", "Umsatz"]);
Object.keys(jeOrt).sort().forEach(function (ort) {
  p.println([ort, jeOrt[ort].toFixed(2).replace(".", ",")]);   // deutsches Komma
});
fs["/beispiele/csv/umsatz-je-ort.csv"] = p;
log.info(p.toString());

23. Einen alten Excel-Export lesen

Der ExcelParser aus dem Modul CSVService liest standardmäßig ISO-8859-1 — so wie ältere Excel-Versionen speichern.

var m = modules.use("CSVService");
var parser = new m.ExcelParser(fs["/beispiele/csv/alt.csv"], { separator: ";" });
var zeile;
while ((zeile = parser.nextLine()) !== null) {
  log.info(zeile.join(" | "));
}

24. Zwei Listen abgleichen

Welche Einträge sind neu dazugekommen, welche fehlen jetzt? Ein Objekt als „Nachschlagetabelle" macht den Vergleich schnell.

var alt = csv.parse("Name\nBäckerei Schmitz\nAutohaus Weber\nBlumen Meyer", { header: true });
var neu = csv.parse("Name\nBäckerei Schmitz\nBlumen Meyer\nCafé Sommer", { header: true });

function namen(liste) {
  var set = {};
  liste.forEach(function (z) { set[z.Name] = true; });
  return set;
}
var altSet = namen(alt), neuSet = namen(neu);

neu.forEach(function (z) { if (!altSet[z.Name]) log.info("neu: " + z.Name); });
alt.forEach(function (z) { if (!neuSet[z.Name]) log.info("entfallen: " + z.Name); });

25. Aufräumen

Löscht den Übungsordner samt Inhalt.

var ordner = fs["/beispiele/csv"];
if (ordner.exists) ordner.remove(true);   // true = mit allem Inhalt
log.info("Übungsordner aufgeräumt");