JSON in Python: Dateien lesen und schreiben, CSV, pathlib und UTF-8-Kodierung
Für JSON in Python gibt es das eingebaute Modul json: json.load liest Daten aus einer geöffneten Datei, json.dump schreibt sie hinein, json.loads und json.dumps erledigen dasselbe mit Strings. Die Datei öffnen Sie mit with open(path, encoding="utf-8"), Pfade bauen Sie bequem mit pathlib.Path, und Tabellen lesen Sie mit dem Modul csv.
Die Arbeit mit JSON in Python braucht keine Fremdbibliotheken: Das Modul json gehört zur Standardbibliothek und verwandelt Dictionaries und Listen in Text und zurück. Daneben braucht man fast immer noch drei Dinge: die Funktion open mit with, das Modul pathlib für Pfade und das Modul csv für Tabellen. Im Folgenden alles der Reihe nach, mit funktionierendem Code und den Stolperfallen, über die man am häufigsten fällt.
Die Funktion open in Python: Modi und Kodierung
Jede Textdatei öffnet man mit der eingebauten Funktion open(path, mode, encoding=...). Sie liefert ein Dateiobjekt mit den Methoden read, readline und write, das Sie zeilenweise in einer Schleife durchlaufen können.
| Modus | Was er macht |
|---|---|
"r" | Lesen (Standard). Fehlt die Datei, gibt es FileNotFoundError |
"w" | Schreiben. Die Datei wird angelegt oder schon beim Öffnen geleert |
"a" | Am Ende anhängen, der vorhandene Inhalt bleibt erhalten |
"x" | Neue Datei anlegen; existiert sie schon, gibt es FileExistsError |
"b" | Zusatz zum Modus: Arbeit mit Bytes ("rb", "wb") |
Wozu with dient
Dateien muss man schließen. Sonst werden Daten unter Umständen nicht auf die Festplatte geschrieben, und das System hält den Dateideskriptor belegt. Die Konstruktion with schließt die Datei automatisch, auch wenn im Block ein Fehler auftritt.
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("Erste Zeile\n")
f.write("Zweite Zeile\n")
with open("notes.txt", encoding="utf-8") as f:
for line in f:
print(line.rstrip("\n"))
Das zeilenweise Durchlaufen liest die Datei Zeile für Zeile und lädt sie nicht vollständig in den Speicher. So verarbeiten Sie sogar Logdateien mit mehreren Gigabyte. Die Methode f.read() liefert den ganzen Inhalt als einen String, f.readlines() eine Liste von Zeilen.
Eine Zeile schreiben, ohne alten Inhalt zu löschen
Der häufigste Anfängerfehler ist, eine Datei mit dem Modus "w" zu öffnen, um eine Zeile „anzuhängen“. Der Modus "w" leert die Datei schon beim Öffnen, noch vor dem ersten write. Zum Anhängen nehmen Sie "a", und um eine fremde Datei sicher nicht zu überschreiben, "x".
with open("log.txt", "a", encoding="utf-8") as f:
f.write("neuer Eintrag\n")
Beachten Sie, dass write keinen Zeilenumbruch hinzufügt. Das "\n" schreiben Sie ausdrücklich. Alternativ können Sie print(..., file=f) verwenden, das setzt den Umbruch für Sie.
UTF-8-Kodierung: warum Zeichen kaputtgehen
Bis Python 3.15 verwendete open() ohne den Parameter encoding die Kodierung des Systems. Unter Linux und macOS ist das UTF-8, unter Windows mit deutschen Spracheinstellungen meist cp1252. Ein Skript, das auf dem Server funktionierte, zeigte auf dem Laptop eines Kollegen dann „ä“ statt „ä“ oder brach mit UnicodeDecodeError ab.
In Python 3.15 ist der UTF-8-Modus nach PEP 686 standardmäßig aktiv: open() ohne encoding liest und schreibt UTF-8 auf allen Systemen. Das alte Verhalten erhalten Sie mit der Umgebungsvariablen PYTHONUTF8=0 oder dem Schalter -X utf8=0. Die Systemkodierung fordern Sie ausdrücklich mit encoding="locale" an.
import sys
print(sys.flags.utf8_mode) # 1 in Python 3.15
open("legacy.txt", encoding="locale") # Systemkodierung
open("old_excel.csv", encoding="cp1252") # Datei, die in cp1252 gespeichert wurde
Die Kehrseite: Eine alte CSV-Datei aus Excel oder einem Windows-Skript, die in cp1252 gespeichert ist, wird jetzt als UTF-8 gelesen und löst UnicodeDecodeError aus. Abhilfe: mit encoding="cp1252" lesen und als UTF-8 neu speichern. Die gute Praxis bleibt dieselbe: Geben Sie encoding immer ausdrücklich an, dann verhält sich der Code in jeder Version gleich.
pathlib in Python: Pfade als Objekte
Pfade aus Strings zusammenzusetzen ("data" + "/" + name) ist mühsam und funktioniert nicht auf allen Systemen gleich. Das Modul pathlib bietet die Klasse Path, bei der Pfade mit dem Operator / verbunden werden und das Objekt aussagekräftige Eigenschaften hat.
from pathlib import Path
data = Path("data")
csv_path = data / "expenses.csv"
print(csv_path.name) # expenses.csv
print(csv_path.stem) # expenses
print(csv_path.suffix) # .csv
print(csv_path.parent) # data
print(csv_path.exists())
data.mkdir(parents=True, exist_ok=True)
(data / "hello.txt").write_text("Hallo", encoding="utf-8")
print((data / "hello.txt").read_text(encoding="utf-8"))
for p in data.glob("*.csv"):
print(p)
Die Methoden read_text und write_text ersetzen bei kleinen Dateien die Kombination aus open und with. Ein Path-Objekt können Sie an open, json und csv übergeben, alle akzeptieren es. Einen Pfad relativ zum Skript statt zum aktuellen Ordner erhalten Sie mit Path(__file__).parent / "data".
In Python 3.14 kamen die Methoden copy() und move() zu Path hinzu, für einfaches Kopieren braucht man also kein shutil mehr. In derselben Version wurde das Modul compression.zstd für die Kompression mit Zstandard in die Standardbibliothek aufgenommen (PEP 784).
JSON in Python: load, dump, loads, dumps
JSON ist ein Textformat für den Datenaustausch: Es wird von APIs geliefert und dient zum Speichern von Konfigurationen und Berichten. Das Modul json kann zwei Dinge: Serialisierung (Python-Objekt wird zu JSON-Text) und Deserialisierung (Text wird zum Objekt). Es gibt vier Funktionen, und die Verwechslung zwischen ihnen ist eine häufige Frage.
| Funktion | Was sie macht |
|---|---|
json.dumps(obj) | Objekt → JSON-String |
json.loads(s) | JSON-String → Objekt |
json.dump(obj, f) | Objekt → in eine geöffnete Datei schreiben |
json.load(f) | JSON aus einer geöffneten Datei lesen |
Einfach zu merken: Das s am Ende steht für string und bedeutet die Arbeit mit Strings. Ohne s arbeiten Sie mit einer Datei.
Zuordnung der Typen
| Python | JSON |
|---|---|
dict | Objekt {} |
list, tuple | Array [] |
str | String |
int, float | Zahl |
True / False | true / false |
None | null |
Ein Tupel wird beim Zurücklesen zu einer Liste. Mengen, Datumswerte und Decimal kennt das Modul nicht und wirft TypeError: Object of type ... is not JSON serializable.
JSON in eine Datei speichern
import json
from pathlib import Path
report = {
"month": "2026-10",
"total": 15230.5,
"categories": {"Essen": 8200, "Verkehr": 2100},
"tags": ["Zuhause", "Arbeit"],
}
path = Path("data/report.json")
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
Der Parameter ensure_ascii=False speichert Umlaute unverändert. Ohne ihn stünde im Dateiinhalt statt „Essen“ eine Escape-Folge wie ä. Das wäre gültiges JSON, aber für Menschen schwer lesbar. indent=2 sorgt für eine übersichtliche Formatierung mit Einrückung.
JSON aus einer Datei lesen
with open("data/report.json", encoding="utf-8") as f:
data = json.load(f)
print(data["total"]) # 15230.5
print(data["categories"]["Essen"]) # 8200
Das Ergebnis ist ein gewöhnliches Dictionary oder eine Liste, mit denen Sie wie gewohnt arbeiten. Ist die Datei kein gültiges oder ein abgeschnittenes JSON, wird json.JSONDecodeError mit Zeilen- und Positionsangabe ausgelöst. Diese Ausnahme sollten Sie abfangen, wenn die Daten von außen kommen.
JSON-String parsen und schön ausgeben
import json
raw = '{"name": "Anna", "skills": ["python", "sql"], "active": true}'
user = json.loads(raw)
print(user["skills"][0]) # python
print(json.dumps(user, ensure_ascii=False, indent=2, sort_keys=True))
sort_keys=True sortiert die Schlüssel bei der Ausgabe, das ist praktisch, um Dateien in git zu vergleichen. Zum schnellen Formatieren einer Datei im Terminal gibt es ein eingebautes Hilfsprogramm: python -m json.tool data/report.json.
Die Antwort einer HTTP-API ist ebenfalls ein JSON-String. Die Bibliothek requests wertet ihn mit response.json() aus, intern passiert dabei dasselbe wie bei json.loads.
Falle: JSON-Schlüssel sind immer Strings
d = {1: "Januar", 2: "Februar"}
back = json.loads(json.dumps(d))
print(back == d) # False
print(back) # {'1': 'Januar', '2': 'Februar'}
# back[1] -> KeyError, richtig ist back["1"]
fixed = {int(k): v for k, v in back.items()}
In JSON können Objektschlüssel nur Strings sein. Numerische Schlüssel werden deshalb nach dem Hin- und Rückweg zu Strings. Wandeln Sie sie beim Lesen wieder um.
Eigene Typen: Datumswerte und Decimal
import json
from datetime import date
def default(obj):
if isinstance(obj, date):
return obj.isoformat()
raise TypeError(f"Kann {type(obj).__name__} nicht serialisieren")
print(json.dumps({"day": date(2026, 10, 1)}, default=default))
# {"day": "2026-10-01"}
Die Funktion im Parameter default wird für jedes unbekannte Objekt aufgerufen. In komplizierteren Fällen können Sie von json.JSONEncoder erben und die Methode default überschreiben.
Ein Element an ein JSON-Array anhängen
Ein JSON-Array lässt sich nicht wie ein Textlog am Ende erweitern: Die Datei muss vollständig gelesen, geändert und wieder geschrieben werden.
path = Path("data/items.json")
items = json.loads(path.read_text(encoding="utf-8")) if path.exists() else []
items.append({"id": len(items) + 1, "title": "neu"})
path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")
Bei vielen Einträgen, die ständig hinzukommen, ist das Format JSON Lines praktischer: ein JSON-Objekt pro Zeile, die Datei wird im Modus "a" geöffnet.
CSV in Python: das Modul csv
CSV ist eine Tabelle in einer Textdatei: Zeilen sind Datensätze, Werte sind durch Kommas getrennt. Das Modul csv behandelt Anführungszeichen, Kommas innerhalb von Werten und Zeilenumbrüche korrekt, was ein handgeschriebenes split(",") nicht leistet.
import csv
with open("data/expenses.csv", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
total = 0.0
for row in reader:
total += float(row["amount"])
print(row["date"], row["category"], row["amount"])
print("Gesamt:", total)
csv.DictReader macht aus jeder Zeile ein Dictionary, die Schlüssel stammen aus der Kopfzeile. Alle Werte sind Strings: Zahlen und Datumswerte wandeln Sie selbst um (float, int, date.fromisoformat). Eine Datei mit Semikolon als Trennzeichen (so speichert deutsches Excel häufig) lesen Sie mit delimiter=";".
rows = [
{"date": "2026-10-01", "category": "Essen", "amount": 450},
{"date": "2026-10-02", "category": "Verkehr", "amount": 60},
]
with open("data/out.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["date", "category", "amount"])
writer.writeheader()
writer.writerows(rows)
Beim Schreiben ist newline="" zwingend: Ohne den Parameter entstehen unter Windows Leerzeilen zwischen den Datensätzen. Wenn die Datei in Excel geöffnet werden soll, verwenden Sie encoding="utf-8-sig". Die BOM-Markierung hilft Excel, UTF-8 zu erkennen.
Von CSV zu JSON: ein vollständiges Beispiel
import csv
import json
from collections import defaultdict
from pathlib import Path
src = Path("data/expenses.csv")
dst = Path("data/report.json")
by_category = defaultdict(float)
with src.open(newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
by_category[row["category"]] += float(row["amount"])
report = {"total": sum(by_category.values()), "categories": dict(by_category)}
dst.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
print(dst.read_text(encoding="utf-8"))
Das ist der Kern jedes Berichtsskripts: Eingabe aus CSV, Zusammenfassung in einem Dictionary, Ausgabe als JSON.
Typische Fehler
FileNotFoundError— der Pfad ist relativ und bezieht sich auf den Ordner, aus dem Sie starten, nicht auf das Skript. Verwenden SiePath(__file__).parent.- Die Datei ist leer, obwohl Sie etwas angehängt haben — Sie haben mit
"w"statt"a"geöffnet. UnicodeDecodeError— die Datei hat eine andere Kodierung; geben Sie sie ausdrücklich an (cp1252,utf-8-sig).TypeError: Object of type set is not JSON serializable— wandeln Sie in eine Liste um oder übergeben Siedefault.KeyErrornach dem Laden von JSON — numerische Schlüssel sind zu Strings geworden.- Die eigene Datei heißt
json.pyodercsv.py— sie verdeckt das Standardmodul, undimport jsonlädt Ihre Datei.
Fazit
Für JSON in Python genügt die Standardbibliothek: json.load und json.dump für Dateien, loads und dumps für Strings, ensure_ascii=False und indent für eine lesbare Ausgabe. Öffnen Sie Dateien mit with und einer ausdrücklichen Kodierung, bauen Sie Pfade mit pathlib, und lesen Sie Tabellen mit csv.DictReader. Im Python-Kurs wird das Thema praktisch behandelt: Der Ausgabentracker liest Ausgaben aus einer CSV-Datei und speichert den Bericht als JSON.
Häufige Fragen
Worin unterscheidet sich json.load von json.loads?
json.load liest JSON aus einem geöffneten Dateiobjekt, json.loads verarbeitet einen String. Ebenso schreibt json.dump in eine Datei, json.dumps liefert einen String zurück.
Wie speichere ich Umlaute in JSON, ohne \u-Folgen zu erhalten?
Übergeben Sie ensure_ascii=False an json.dump oder json.dumps und öffnen Sie die Datei mit encoding="utf-8".
Muss ich eine Bibliothek für JSON in Python installieren?
Nein, das Modul json gehört zur Standardbibliothek. Es genügt import json.
Kann ich Datumswerte oder Mengen direkt in JSON speichern?
Nicht direkt. Sie müssen sie umwandeln, etwa ein Datum mit isoformat in einen ISO-String und eine Menge in eine Liste, oder eine Funktion an den Parameter default übergeben.
Wie lese ich ein großes JSON, das nicht in den Speicher passt?
Das Standardmodul json.load liest die Datei vollständig. Bei großen Datenmengen speichern Sie die Daten im Format JSON Lines und verarbeiten sie Zeile für Zeile mit json.loads.
Dieses Thema wird im Kurs „Python 3.15: от новичка до профи“ ausführlich mit Praxis behandelt.
Zum Kurs