SemaTrain Logo Ein Fachportal von SemaTrain

Dateien & Textdaten: CSV/JSON + RegEx (Python)

Sie verarbeiten Dateien und Textdaten sicher: lesen/schreiben, CSV, JSON und RegEx – praxisnah mit SemaTrain-Beispieldaten (z.B. Kurszeilen, Orte, Preise).

Hinweis: Beispiele nutzen Beispieldaten. Fokus: robuste Verarbeitung (Encoding, Fehlerfälle, saubere Patterns).

Python Schulung – Kursbezug

Dieses Kapitel ist Teil des Lernpfads zur Python Schulung. Termine & Buchung laufen über SemaTrain.de.

Ziel: Sie können Text-/Dateidaten robust einlesen, strukturieren (CSV/JSON) und mit RegEx gezielt extrahieren.

Merksatz: Erst strukturiert (CSV/JSON) arbeiten – RegEx ist super für Text, aber kein Ersatz für saubere Formate.

Worum geht’s?

Lehr-/Lernziele

Nach diesem Kapitel können Sie …

Wie Sie die Ziele erreichen: Wir verarbeiten „Kurszeilen“ im SemaTrain-Kontext (Beispieldaten). Am Ende bauen Sie einen Mini-Parser: CSV → Auswertung → JSON-Export + RegEx-Checks.

Pfade & Textdateien (pathlib + utf-8)

pathlib: write_text/read_text (LZ1) (Python)
from pathlib import Path

basis_ordner = Path("daten")          # relativer Ordner
basis_ordner.mkdir(exist_ok=True)

dateipfad = basis_ordner / "notiz.txt"

# Schreiben
text = "SemaTrain | Python Schulung\nOrt: Hamburg\nFormat: Online\n"
dateipfad.write_text(text, encoding="utf-8")

# Lesen
inhalt = dateipfad.read_text(encoding="utf-8")
print(inhalt)

# Robust: Existenz prüfen
if not dateipfad.exists():
    print("Datei fehlt!")
Typische Fehlerfälle (Encoding/Datei fehlt)
try/except: FileNotFoundError + UnicodeDecodeError (LZ1) (Python)
from pathlib import Path

dateipfad = Path("daten") / "unbekannt.txt"

try:
    inhalt = dateipfad.read_text(encoding="utf-8")
except FileNotFoundError:
    print("Datei nicht gefunden:", dateipfad)
except UnicodeDecodeError:
    print("Encoding-Problem: Datei ist vermutlich nicht utf-8.")

CSV lesen/schreiben (DictReader/DictWriter)

CSV ist „einfach“, aber achten Sie auf Trennzeichen (Komma/Semikolon) und Encoding.

CSV schreiben (Delimiter ;) (LZ2) (Python)
import csv
from pathlib import Path

dateipfad = Path("daten") / "kurse.csv"

felder = ["kurs", "standort", "format", "dauer_tage", "preis_euro"]
zeilen = [
    {"kurs":"Python Schulung","standort":"Hamburg","format":"Praesenz","dauer_tage":"3","preis_euro":"1490.00"},
    {"kurs":"Python Schulung","standort":"Berlin","format":"Online","dauer_tage":"3","preis_euro":"1390.00"},
    {"kurs":"Python Schulung","standort":"Koeln","format":"Online","dauer_tage":"2","preis_euro":"990.00"},
]

with dateipfad.open("w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=felder, delimiter=";")
    writer.writeheader()
    writer.writerows(zeilen)

print("CSV geschrieben:", dateipfad)
CSV lesen + Typkonvertierung (LZ2) (Python)
import csv
from pathlib import Path

dateipfad = Path("daten") / "kurse.csv"

with dateipfad.open("r", newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f, delimiter=";")
    for zeile in reader:
        # Strings -> Typen (Beispiel)
        dauer_tage = int(zeile["dauer_tage"])
        preis_euro = float(zeile["preis_euro"])
        print(zeile["standort"], zeile["format"], dauer_tage, preis_euro)

JSON laden/speichern

JSON dump/load (LZ3) (Python)
import json
from pathlib import Path

dateipfad = Path("daten") / "auswertung.json"

auswertung = {
    "anbieter": "SemaTrain",
    "kurs": "Python Schulung",
    "standorte": ["Hamburg", "Berlin", "Koeln"],
    "hinweis": "Beispieldaten",
}

# Speichern (pretty)
dateipfad.write_text(json.dumps(auswertung, ensure_ascii=False, indent=2), encoding="utf-8")

# Laden
geladen = json.loads(dateipfad.read_text(encoding="utf-8"))
print(geladen["anbieter"], geladen["standorte"])

RegEx (re): Finden, Extrahieren, Validieren

Extraktion (Preis/PLZ)

re.search + Gruppen (LZ4) (Python)
import re

text = "Python Schulung | PLZ 20095 | Preis 1490.00 EUR | Kontakt: training@sematrain.de"

muster_preis = r"Preis\s+(\d+(?:\.\d{2})?)\s*EUR"
muster_plz   = r"PLZ\s+(\d{5})"

preis = re.search(muster_preis, text)
plz   = re.search(muster_plz, text)

print("Preis:", float(preis.group(1)) if preis else None)
print("PLZ:", plz.group(1) if plz else None)

Einfache Validierung (E-Mail)

re.match für Validierung (LZ4) (Python)
import re

def ist_email(text: str) -> bool:
    # pragmatisch (kein RFC-Monster)
    muster = r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
    return re.match(muster, text) is not None

tests = ["training@sematrain.de", "info@", "max@firma", "max@firma.de"]
for t in tests:
    print(t, "->", ist_email(t))
Bonus: re.findall + Named Groups
findall/finditer + Named Groups (LZ4) (Python)
import re

text = """
Kurs=Python Schulung; Ort=Hamburg; Format=Online; Preis=1390.00
Kurs=Python Schulung; Ort=Berlin;  Format=Online; Preis=1290.00
"""

muster = r"Ort=(?P<ort>[A-Za-z]+);\s*Format=(?P<format>[A-Za-z]+);\s*Preis=(?P<preis>\d+\.\d{2})"

treffer = re.findall(muster, text)
print("Findall (Tupel):", treffer)

for m in re.finditer(muster, text):
    print("Named:", m.group("ort"), m.group("format"), float(m.group("preis")))

Praxisaufgabe (Mini)

Sie bauen eine kleine Pipeline: CSV → Berechnung → JSON-Export + RegEx-Checks.

Beitrag zu den Lehr-/Lernzielen: LZ1 (Pfade/Dateien), LZ2 (CSV), LZ3 (JSON), LZ4 (RegEx).

  1. (LZ2) Lesen Sie kurse.csv (Delimiter „;“) und konvertieren Sie dauer_tage/preis_euro.
  2. (LZ3) Berechnen Sie tagespreis = preis_euro / dauer_tage und speichern Sie eine Ergebnisliste als JSON.
  3. (LZ4) Validieren Sie pro Datensatz eine Kontakt-E-Mail (z.B. extra Spalte kontakt) mit RegEx.
  4. (Bonus) Extrahieren Sie aus einer Textzeile „PLZ 12345“ die PLZ (RegEx-Gruppe).
Lösungsvorschlag anzeigen
Lösung: CSV → JSON + RegEx (Beispieldaten) (Python)
import csv
import json
import re
from pathlib import Path

def ist_email(text: str) -> bool:
    muster = r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
    return re.match(muster, text) is not None

eingabe = Path("daten") / "kurse.csv"
ausgabe = Path("daten") / "kurse_auswertung.json"

ergebnisse = []

with eingabe.open("r", newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f, delimiter=";")
    for zeile in reader:
        kurs = zeile["kurs"]
        standort = zeile["standort"]
        format_ = zeile["format"]

        dauer_tage = int(zeile["dauer_tage"])
        preis_euro = float(zeile["preis_euro"])

        # (Beispiel) Kontakt könnte aus CSV kommen – hier setzen wir beispielhaft:
        kontakt = zeile.get("kontakt") or "training@sematrain.de"

        datensatz = {
            "kurs": kurs,
            "standort": standort,
            "format": format_,
            "dauer_tage": dauer_tage,
            "preis_euro": preis_euro,
            "tagespreis_euro": round(preis_euro / dauer_tage, 2),
            "kontakt": kontakt,
            "kontakt_ok": ist_email(kontakt),
        }
        ergebnisse.append(datensatz)

ausgabe.write_text(json.dumps(ergebnisse, ensure_ascii=False, indent=2), encoding="utf-8")
print("JSON gespeichert:", ausgabe)

# Bonus: PLZ aus Text extrahieren
text = "Ort: Hamburg | PLZ 20095 | Preis 1490.00 EUR"
m = re.search(r"PLZ\s+(\d{5})", text)
print("PLZ:", m.group(1) if m else None)

Kurz-Takeaways

Quiz: Dateien, CSV/JSON & RegEx

1. (LZ1) Welche Kombination ist in Python die sauberste für Pfade + Encoding?

2. (LZ2) Womit lesen Sie CSV-Zeilen direkt als Dict (Header → Keys)?

3. (LZ3) Welche Funktion speichert Python-Daten strukturiert als JSON-Text?

4. (LZ4) Wofür nutzen Sie Klammern (...) in einer RegEx typischerweise?

Praxisaufgabe

Mini-Projekt: Kurs-Import (CSV → Clean CSV + JSON) + RegEx-Checks

Sie bauen eine kleine Import-Pipeline (Beispieldaten): CSV wird eingelesen, Typen werden konvertiert, Textfelder werden mit RegEx geprüft/ausgelesen (E-Mail, PLZ, Preis), anschließend werden Clean-CSV und JSON exportiert – plus kurzer Konsolen-Report.

Beitrag zu den Lehr-/Lernzielen:
LZ1 (Pfade/Datei-IO + Fehlerfälle), LZ2 (CSV DictReader/Writer), LZ3 (JSON Export), LZ4 (RegEx Extraktion + Validierung).

Aufgabe

Lösung anzeigen
Lösung: CSV → Validierung/Extraktion → Clean CSV + JSON (Beispieldaten) (Python)
import csv
import json
import re
from pathlib import Path

# ===== RegEx-Utilities (LZ4) =====
EMAIL_RE = re.compile(r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$")
PLZ_RE   = re.compile(r"\b(\d{5})\b")
PREIS_RE = re.compile(r"Preis\s+(\d+(?:\.\d{2})?)\s*EUR", re.IGNORECASE)

def ist_email(text: str) -> bool:
    return EMAIL_RE.match(text.strip()) is not None

def finde_plz(text: str) -> str | None:
    m = PLZ_RE.search(text or "")
    return m.group(1) if m else None

def finde_preis(text: str) -> float | None:
    m = PREIS_RE.search(text or "")
    return float(m.group(1)) if m else None

# ===== Pfade (LZ1) =====
basis = Path("daten")
basis.mkdir(exist_ok=True)

eingabe = basis / "kurse.csv"
clean_csv = basis / "kurse_clean.csv"
export_json = basis / "kurse_export.json"

# ===== (Optional) Sample-CSV erzeugen, falls Datei fehlt (LZ1/LZ2) =====
if not eingabe.exists():
    felder = ["kurs","standort","format","dauer_tage","preis_euro","kontakt","info"]
    sample = [
        {"kurs":"Python Schulung","standort":"Hamburg","format":"Praesenz","dauer_tage":"3","preis_euro":"1490.00",
         "kontakt":"training@sematrain.de","info":"Ort: Hamburg | PLZ 20095 | Preis 1490.00 EUR"},
        {"kurs":"Python Schulung","standort":"Berlin","format":"Online","dauer_tage":"3","preis_euro":"1390.00",
         "kontakt":"info@sematrain.de","info":"Ort: Berlin | PLZ 10115"},
        {"kurs":"Python Schulung","standort":"Koeln","format":"Online","dauer_tage":"2","preis_euro":"990.00",
         "kontakt":"info@","info":"Ort: Koeln | PLZ 50667 | Preis 990.00 EUR"},  # absichtlich "bad email"
    ]
    with eingabe.open("w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=felder, delimiter=";")
        w.writeheader()
        w.writerows(sample)
    print("Sample CSV erzeugt:", eingabe)

# ===== CSV einlesen + verarbeiten (LZ2) =====
ergebnisse = []
fehler_email = 0
fehlende_plz = 0

try:
    with eingabe.open("r", newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f, delimiter=";")
        for row in reader:
            kurs = (row.get("kurs") or "").strip()
            standort = (row.get("standort") or "").strip()
            format_ = (row.get("format") or "").strip()

            # Typen robust konvertieren
            try:
                dauer_tage = int((row.get("dauer_tage") or "0").strip())
            except ValueError:
                dauer_tage = 0

            try:
                preis_euro = float((row.get("preis_euro") or "0").strip())
            except ValueError:
                preis_euro = 0.0

            kontakt = (row.get("kontakt") or "").strip()
            info = (row.get("info") or "").strip()

            kontakt_ok = ist_email(kontakt)
            if not kontakt_ok:
                fehler_email += 1

            plz = finde_plz(info)
            if plz is None:
                fehlende_plz += 1

            preis_im_text = finde_preis(info)  # optionaler Bonus
            tagespreis = round(preis_euro / dauer_tage, 2) if dauer_tage > 0 else None

            ergebnisse.append({
                "kurs": kurs,
                "standort": standort,
                "format": format_,
                "dauer_tage": dauer_tage,
                "preis_euro": preis_euro,
                "tagespreis_euro": tagespreis,
                "kontakt": kontakt,
                "kontakt_ok": kontakt_ok,
                "plz": plz,
                "preis_im_text": preis_im_text,
            })

except FileNotFoundError:
    print("Datei fehlt:", eingabe)
    raise
except UnicodeDecodeError:
    print("Encoding-Problem: Datei ist vermutlich nicht utf-8.")
    raise

# ===== Clean CSV schreiben (LZ2) =====
felder_out = [
    "kurs","standort","format","dauer_tage","preis_euro","tagespreis_euro",
    "kontakt","kontakt_ok","plz","preis_im_text"
]
with clean_csv.open("w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=felder_out, delimiter=";")
    w.writeheader()
    w.writerows(ergebnisse)

# ===== JSON exportieren (LZ3) =====
export_json.write_text(
    json.dumps(ergebnisse, ensure_ascii=False, indent=2),
    encoding="utf-8"
)

# ===== Mini-Report (Bonus, aber sehr hilfreich) =====
print("Einträge:", len(ergebnisse))
print("E-Mail fehlerhaft:", fehler_email)
print("PLZ fehlend:", fehlende_plz)
print("Clean CSV:", clean_csv)
print("JSON:", export_json)