Daten aus .csv in .txt vorlage

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
jim_beam
User
Beiträge: 5
Registriert: Dienstag 8. August 2023, 08:03

Hallo Zusammen, ich bin blutiger Phyton Anfänger und hoffe ich könnt mir helfen.

Ich versuche Daten aus einer semikolon separierten .csv an die Stelle der Platzhalter in einer .txt zu schreiben. Solange ich in meiner .csv nur einen Splatenkopf habe funktioniert das Skript. Sobald ich aber in Spalte B (hier "vlan") einen weiteren Wert hinzufüge bricht das Skript mit folgenden Fehler ab:

Code: Alles auswählen

PS C:\Program Files\WPy64-31110\scripts> python test.py
Traceback (most recent call last):
  File "C:\Program Files\WPy64-31110\scripts\test.py", line 27, in <module>
    generate_router_config(csv_data, router_template_file, router_config_output_file)
  File "C:\Program Files\WPy64-31110\scripts\test.py", line 18, in generate_router_config
    config_entry = config_template.format(**entry)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'routername'

Hier mein skript:

Code: Alles auswählen

import csv

def read_csv_data(file_path):
    data = []
    with open(file_path, 'r') as csv_file:
        csv_reader = csv.DictReader(csv_file, delimiter=';')
        for row in csv_reader:
            data.append(row)
    return data

def generate_router_config(csv_data, template_file, output_file):
    with open(template_file, 'r') as template:
        config_template = template.read()

    with open(output_file, 'w') as output:
        for entry in csv_data:
            print(entry)  # Ausgabe der entry-Daten zur Überprüfung
            config_entry = config_template.format(**entry)
            output.write(config_entry)

if __name__ == "__main__":
    csv_file_path = r"C:\Program Files\WPy64-31110\scripts\test.csv"
    csv_data = read_csv_data(csv_file_path)
        router_template_file = r"C:\Program Files\WPy64-31110\scripts\test_vorlage.txt"
        router_config_output_file = "router_konfiguration.txt"
        csv_data = read_csv_data(csv_file_path)
        generate_router_config(csv_data, router_template_file, router_config_output_file)
Die .csv wurde mit Excel365 erstellt und als Typ "CSV Trennzeichen-getrennt" gespeichert. Die Zellen habe ich alle als Text formatiert.
Inhalt der .csv:

Code: Alles auswählen

routername;vlan
testname;1
Inhalt der meiner test_vorlage.txt:

Code: Alles auswählen

{routername}
{vlan}

Hat jemand einen Tip für mich?
Vielen Dank im Vorraus!
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Eingerückt wird immer mit 4 Leerzeichen pro Ebene; da Deine Einrückungen kaputt sind, kann man nicht sagen, welches Programm Du wirklich ausgeführt hast.
csv-Dateien sollte man immer mit newline="" öffnen.

Code: Alles auswählen

def read_csv_data(file_path):
    with open(file_path, 'r', newline='') as csv_file:
        return list(csv.DictReader(csv_file, delimiter=';'))
Wie ist die Ausgabe von `entry`? Ist da wirklich der Schlüssel routername drin?
jim_beam
User
Beiträge: 5
Registriert: Dienstag 8. August 2023, 08:03

Ok danke, ich habe die Einrücken korrigiert und öffne die .csv mit newline.
Leider kommt keine Ausgabe von `entry`. Es erscheint lediglich der o.g. Fehler
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was heisst es kommt keine Ausgabe von entry? Die kommt ja nicht von alleine, die musst du da schon reinschreiben. Bitte zeig den konkreten Code, den du verwendest (nach der Aenderung). Umschreibungen, was da jetzt veraendert wurde, helfen nicht.
jim_beam
User
Beiträge: 5
Registriert: Dienstag 8. August 2023, 08:03

Hier mein angepasstest Script:

Code: Alles auswählen

import csv

def read_csv_data(file_path):
    with open(file_path, 'r', newline='') as csv_file:
        return list(csv.DictReader(csv_file, delimiter=';'))

def generate_router_config(csv_data, template_file, output_file):
    with open(template_file, 'r') as template:
        config_template = template.read()

    with open(output_file, 'w') as output:
        for entry in csv_data:
            print(entry)  # Ausgabe der entry-Daten zur Überprüfung
            config_entry = config_template.format(**entry)
            output.write(config_entry)

if __name__ == "__main__":
    csv_file_path = r"C:\Program Files\WPy64-31110\scripts\test.csv"
    csv_data = read_csv_data(csv_file_path)
        router_template_file = r"C:\Program Files\WPy64-31110\scripts\test_vorlage.txt"
        router_config_output_file = "router_konfiguration.txt"
        csv_data = read_csv_data(csv_file_path)
        generate_router_config(csv_data, router_template_file, router_config_output_file)

Die Fehlermeldung beim Ausführen lautet weiterhin:

Code: Alles auswählen

PS C:\Program Files\WPy64-31110\scripts> python test.py
Traceback (most recent call last):
  File "C:\Program Files\WPy64-31110\scripts\test.py", line 27, in <module>
    generate_router_config(csv_data, router_template_file, router_config_output_file)
  File "C:\Program Files\WPy64-31110\scripts\test.py", line 18, in generate_router_config
    config_entry = config_template.format(**entry)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'routername'
Die Ausgabe von entry müsste doch beim Befehl absenden mit ausgegeben werden oder verstehe ich das falsch?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ja, das print-statement sollte ausgefuehrt werden. Aber das ist natuerlich immer noch nicht die Datei, die du wirklich ausfuehrst. Denn wenn ich die in eine Datei kopiere, und mir die Zeile 18 anschaue, dann ist das

Code: Alles auswählen

router_template_file = r"C:\Program Files\WPy64-31110\scripts\test_vorlage.txt"
und nicht die Zeile, die da bei dir steht. Da ist also noch irgendetwas faul, von dem wir noch nichts wissen.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jim_beam: Zeig doch erst einmal den tatsächlich ausgeführten Quelltext. Der im Beitrag kann so nicht gelaufen sein, der hat einen offensichtlichen Syntaxfehler. Und der Traceback sagt was von Zeile 27, bei Quelltext der nur 23 Zeilen lang ist. Das passt nicht zusammen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jim_beam: Allgemeine Anmerkungen zum Quelltext: Das Hauptprogramm sollte in einer Funktion stecken. Im Moment werden da auf Modulebene Namen definiert, die dann in Funktionen auch noch mal als lokale Namen verwendet werden. Hoffentlich — denn bei so etwas ist die Gefahr gross, dass man irgendwann mal in Funktionen einen Namen ausserhalb der Funktion verwendet ohne das zu merken und sich wundert warum der nicht den Wert hat, den man erwartet.

Wenn man Quelltext für andere zum Testen veröffentlich sind irgendwo im Programm verstreute absolte Pfade auf einem Windowssystem nicht hilfreich. Und das gilt eigentlich auch schon für eigene Tests. Der Basispfad, den alle gemeinsam haben sollte man auch nicht mehrfach schreiben, sondern als Konstante definieren.

Beim öffnen von Textdateien sollte man immer die Kodierung angeben die der Text hat, sonst ”rät” Python das anhand der Umgebung, das muss aber für konkrete Dateien gar nicht stimmen.

Es werden Werte `*_file` genannt die gar keine Dateien sind sondern Datei*namen*. Bei einer Datei erwartet man ein Objekt mit `read()`- und/oder `write()`- und `close()`-Methoden, keine Zeichenkette (oder ein `Path`-Objekt).

`csv_data` mag in der Hauptfunktion noch gehen, aber in `generate_router_config()` ist es völlig egal wo die Daten mal herkamen, aus einer CSV- oder JSON-Datei oder aus einer Datenbank, oder direkt im Quelltext als Datenstruktur ist für die Arbeit dieser Funktion völlig egal.

Man muss auch nicht jeden kleinen Zwischenwert an einen Namen binden. Das kann die Lesbarkeit verschlechtern.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import csv
from pathlib import Path

BASE_PATH = Path(R"C:\Program Files\WPy64-31110\scripts")


def read_csv_data(file_path):
    with open(file_path, "r", newline="", encoding="utf-8") as csv_file:
        return list(csv.DictReader(csv_file, delimiter=";"))


def generate_router_config(entries, template_file_path, output_file_path):
    config_template = template_file_path.read_text(encoding="utf-8")
    with open(output_file_path, "w", encoding="utf-8") as file:
        file.writelines(map(config_template.format_map, entries))


def main():
    entries = read_csv_data(BASE_PATH / "test.csv")
    generate_router_config(
        entries,
        BASE_PATH / "test_vorlage.txt",
        Path("router_konfiguration.txt"),
    )


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
jim_beam
User
Beiträge: 5
Registriert: Dienstag 8. August 2023, 08:03

Vielen Dank für die Anmerkungen.

Das Problem lag in Notepad++! Alle Änderungen die ich da an der test.py vorgenommen habe, wurde wohl nicht in die datei in meinem python pfad geschrieben. Mir ist auch nicht aufgefallen, dass die Fehlermeldung auf eine Zeile verweißt, die eigentlich garnicht existiert.

Nun funktioniert es! :D
jim_beam
User
Beiträge: 5
Registriert: Dienstag 8. August 2023, 08:03

Nach weiterer Überlegung ist es sehr unpraktisch, dass das Script sofort abbricht, sobald im Tabellenkopf der csv ein Wert steht welcher in der vorlage.txt nicht zu finden ist.
Kann man das script evtl. so anpassen, dass Variablen die nicht in der vorlage.txt stehen aber in der csv stehen einfach ignoriert werden?
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Da bricht nichts ab, Werte, die nicht gebraucht werden, werden einfach ignoriert:

Code: Alles auswählen

"{da}".format_map({"da": 17, "nicht_da": 29}) # -> '17'
Antworten