Python-Kurs / CSV-Daten prüfen und nachvollziehbare Berichte erstellen
Ein CSV-Berichtsprogramm sollte Überschriften, Pflichtfelder, Beträge und doppelte Kennungen prüfen, bevor es gültige Datensätze aufsummiert. Das folgende vollständige Projekt schreibt zusätzlich die Ablehnungsgründe und liefert bei verworfenen Datensätzen einen Fehlerstatus, damit eine Teilauswertung nicht als vollständiger Erfolg gilt.
Eingaben, Ausgaben und Ablehnungsregel festlegen
Eine Ausgabe besteht aus id, category und amount. Die Kennung identifiziert einen Datensatz, die Kategorie bestimmt die Berichtssumme, und der Betrag muss aus mindestens einer ASCII-Ziffer bestehen. Null ist erlaubt; Minuszeichen, Dezimaltrennzeichen und Tausendertrennzeichen sind es nicht. Alle Beträge müssen bereits in derselben fachlichen Einheit vorliegen.
Die erste gültige und angenommene Zeile beansprucht ihre Kennung. Spätere Zeilen mit derselben Kennung werden verworfen. Eine vorherige ungültige Zeile beansprucht die Kennung dagegen nicht. Leerraum am Anfang und Ende der drei Werte wird entfernt. Dadurch sind etwa e1 und e1 nach dieser Normalisierung dieselbe Kennung.
Verwenden Sie einen neuen Übungsordner mit UTF-8-Dateien. expenses-input.csv bleibt unverändert; report.csv und rejected.csv werden neu erzeugt. Sind Ausgaben bereits vorhanden, beendet sich das Programm. Legen Sie korrigierte Eingaben für einen neuen Vergleich in einen weiteren Ordner, statt die Überschreibsperre zu entfernen.
Die Grundlagen zum Trennzeichen und zu mehrzeiligen Feldern vermittelt der Kurs zum Lesen und Schreiben von CSV-Dateien. Die Python-Referenz zum CSV-Modul erläutert Reader, Writer und ihre Feldbehandlung.
Die vollständige Testeingabe vorbereiten
Speichern Sie die folgende Datei als expenses-input.csv:
id,category,amount
e1,food,3500
e2,food,8000
e3,transport,2500
e2,food,8000
e4,books,-1
Die doppelte Kennung e2 und der negative Betrag für e4 sind absichtlich enthalten. Die erste gültige e2-Zeile soll angenommen werden, die zweite nicht. Der Datensatz mit -1 erfüllt die Betragsregel nicht. Erfolgreiche Ablehnung dieser Zeilen ist Teil der Übung; die Datenfehler sollen nicht stillschweigend verschwinden.
Die Kopfzeile muss genau die drei angegebenen Namen in dieser Reihenfolge enthalten. Großschreibung, zusätzliche Spalten oder eine andere Reihenfolge sind kein unterstütztes alternatives Schema. Die Eingabekodierung utf-8-sig im Programm akzeptiert UTF-8 sowohl mit als auch ohne anfängliche Byte Order Mark.
Das Berichtsprogramm ausführen und beide Ergebnisse lesen
Speichern Sie daneben das vollständige Programm csv_report.py. Es benötigt nur die Standardbibliothek:
import csv
import re
from collections import defaultdict
from pathlib import Path
source = Path("expenses-input.csv")
report = Path("report.csv")
rejected = Path("rejected.csv")
if report.exists() or rejected.exists():
raise FileExistsError("Use a new output directory")
totals = defaultdict(int)
seen = set()
errors = []
accepted = 0
with source.open(encoding="utf-8-sig", newline="") as f:
reader = csv.DictReader(f)
if reader.fieldnames != ["id", "category", "amount"]:
raise ValueError("unexpected header")
for number, row in enumerate(reader, start=2):
key = (row.get("id") or "").strip()
category = (row.get("category") or "").strip()
amount_text = (row.get("amount") or "").strip()
reason = ""
if None in row or not key or not category:
reason = "invalid fields"
elif key in seen:
reason = "duplicate id"
elif not re.fullmatch(r"[0-9]+", amount_text):
reason = "invalid amount"
if reason:
errors.append({"record": number, "reason": reason})
continue
seen.add(key)
totals[category] += int(amount_text)
accepted += 1
with report.open("x", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["category", "total"])
writer.writerows(sorted(totals.items()))
with rejected.open("x", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["record", "reason"])
writer.writeheader()
writer.writerows(errors)
print("accepted:", accepted, "rejected:", len(errors))
print("total:", sum(totals.values()))
raise SystemExit(1 if errors else 0)
Starten Sie mit der gewählten Projektumgebung im Ordner dieser Eingabedatei:
python csv_report.py
Unter macOS oder Ubuntu kann außerhalb einer virtuellen Umgebung der Interpreterbefehl python3 heißen. Erwartet werden accepted: 3 rejected: 2 und total: 14000. Der Prozessstatus ist eins, weil zwei Datensätze verworfen wurden. Prüfen Sie ihn unmittelbar danach beispielsweise mit $LASTEXITCODE in PowerShell oder echo $? in einer üblichen macOS-/Ubuntu-Shell.
report.csv enthält vollständig:
category,total
food,11500
transport,2500
rejected.csv enthält vollständig:
record,reason
5,duplicate id
6,invalid amount
Die Summe 14.000 ergibt sich aus den drei angenommenen Beträgen 3500, 8000 und 2500. Sie ist keine Summe aller gelieferten Zeilen. Der Bericht sortiert die Kategorien ausdrücklich; die Ablehnungsdatei enthält nur Datensatznummer und Grund, nicht den vollständigen verworfenen Datensatz. Zur Korrektur benötigen Sie daher weiterhin die unveränderte Eingabedatei.
record zählt logische CSV-Datensätze mit der Kopfzeile als Nummer eins. Der doppelte Datensatz erhält Nummer fünf, der negative Betrag Nummer sechs. Ein in Anführungszeichen eingeschlossener Zeilenumbruch innerhalb einer Kategorie erzeugt keinen zusätzlichen Datensatz; physische Textzeilen und diese Nummern können deshalb auseinanderfallen.
Die Prüfung None in row erkennt zusätzliche Felder, die DictReader unter einem besonderen Schlüssel ablegt. Fehlende oder leere Kennungen beziehungsweise Kategorien werden als invalid fields gemeldet. Ein fehlender Betragswert wird als leerer Text behandelt und scheitert danach als invalid amount. Bei mehreren Problemen entscheidet die Reihenfolge der Bedingungen über den ausgegebenen Grund.
Wiederholung, Grenzfälle und teilweise Ausgaben prüfen
- Vergleichen Sie die Kategoriesummen mit den gültigen Quelldatensätzen. Prüfen Sie zusätzlich beide Ablehnungsgründe und den Prozessstatus eins.
- Erstellen Sie in einem weiteren neuen Ordner eine korrigierte Eingabe ohne die doppelte Zeile und mit einem gültigen Betrag für
e4. Starten Sie dort die unveränderte Programmdatei. Es darf kein Datensatz verworfen werden; der Status muss null sein. Die Ablehnungsdatei enthält dann weiterhin ihre Kopfzeile. - Ändern Sie in einem weiteren Versuch nur die Reihenfolge der Kopfspalten. Das Programm muss
unexpected headermelden, bevor die beiden Berichte geschrieben werden. Eine erfolgreiche vorherige Auswertung belegt nicht, dass jedes Schema akzeptiert wird. - Wiederholen Sie einen bereits abgeschlossenen Lauf im selben Ausgabeordner. Die vorhandenen Ergebnisdateien müssen erhalten bleiben;
FileExistsErrorist die hier vorgesehene Reaktion.
Die beiden Ausgabedateien werden nacheinander erstellt und bilden keine atomare Transaktion. Scheitert das zweite Schreiben, kann der erste Bericht bereits existieren. Er ist damit noch kein vollständiges Ausgabepaket. Für eine praktische Pipeline können Sie in ein eigenes temporäres Verzeichnis schreiben, beide Ergebnisse prüfen und erst danach einen klar gekennzeichneten abgeschlossenen Satz weitergeben.
Sehr große Dateien erfordern außerdem passende Grenzen und Ressourcenplanung. Die Kategoriesummen, angenommenen Kennungen und Ablehnungsgründe werden im Speicher gesammelt; das Programm ist nicht allein durch den zeilenweisen Reader unbegrenzt speichersparend. Eine extrem lange Ziffernfolge kann auch die Ganzzahlumwandlung überfordern und den gesamten Lauf abbrechen. Ergänzen Sie für reale Eingaben einen sinnvollen fachlichen Höchstbetrag und eine begrenzte Ziffernzahl.
CSV-Escaping sorgt für gültige Felder, verhindert aber nicht jede Interpretation durch eine Tabellenkalkulation. Eine externe Kategorie, die wie eine Formel beginnt, muss beim Import gemäß dem Zielprogramm behandelt werden. LibreOffice beschreibt dafür unter anderem die Option zur Formelauswertung beim Textimport. Legen Sie eine solche Importregel fest, bevor Sie Berichte mit fremden Textwerten verteilen.
Eine automatisierte Prüfung des gemeinsamen Verhaltens von Prozessstatus, Bericht und Ablehnung finden Sie im Kurs zu Einheitstests und CSV-Integration. Prüfen Sie dabei stets auch die Fehlerfälle; ein korrekter Teilbetrag reicht als Erfolgsnachweis nicht aus.




