Search & Replace-Befehl für Änderung des Quelltextes

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
Ambig
User
Beiträge: 2
Registriert: Dienstag 14. April 2015, 08:38

Schönen guten Tag an alle, die dieses Thema lesen!

Ich wende mich an euch in der Hoffnung, dass die Community hier mit ihrer Phyton-Kompetenz mir bei meinem Problem weiterhelfen kann. Ich selbst besitze nur (mangelhafte :K ) rudimentäre Kenntnisse in Java (ich komme eigentlich aus dem Bereich des Maschinenbaus) und bin bei meinem Projekt auf Python angewiesen. Bisher habe ich versucht mit Tutorials und Lesen von einigen Foreneinträgen durch Trial&Error mich meinem Ziel anzunähern, jedoch ohne Erfolg bisher.
Ziel ist es einen Quellcode in Phyton zu verfassen, der in den Quelltext der FE-Simulation gezielt in eine Zeile eingreift, um die Parameter durch die Parameter-Zeile aus einer anderen „Hilfsdatei“ zu ersetzen. Dieser Vorgang sollte mehrfach wiederholt werden. Ich schreibe mal hier auf wie mich das vorgestellt habe:

I. FE-Simulation wird zum Zeitpunkt t beendet
II. Python-Quellcode „greift“ ein
1. Öffne Datei „FE-Simulation.k“
2. Öffne Datei „Hilfsdatei.txt“
3. Suche Zeile X in „Hilfsdatei.txt“
4. Ersetze Zeile Y in „FE-Simulation.k“ durch Zeile X aus „Hilfsdatei.txt“ - Bei dem Inhalt der Zeile handelt es sich um Fließkommazahlen, die so wie sie in der Hilfsdatei.txt stehen (inkl. Leerzeichen) übermittelt werden müssen. Z.B.:
Zeile X: " 0.100000 0.000 0.000 0.000 0.000 0 0.0001.0000E+20"
Zeile Y: " 0.200000 0.000 0.000 0.000 0.000 0 0.0001.0000E+20"
III. FE-Simulation startet mit den substituierten Parametern zum Zeitpunkt t erneut
IV. FE-Simulation endet mit den substituierten Parametern zum Zeitpunkt t+1
1. Suche Zeile X+1 in „Hilfsdatei.txt“
2. Ersetze Zeile Y in „FE-Simulation.k“ durch Zeile X+1 aus „Hilfsdatei.txt“
V. FE-Simulation startet mit den substituierten Parametern zum Zeitpunkt t+1 erneut
VI. FE-Simulation endet mit den substituierten Parametern zum Zeitpunkt t+2
VII. Das Substituieren der Zeilen geht so lange bis alle Zeilen aus der „Hilfsdatei.txt“ nacheinander ersetzt wurden und die Simulation beendet ist
1. Abbruchkriterium für Substitution erreicht
2. Schließe Datei „Hilfsdatei.txt“
3. Schließe Datei „FE-Simulation.k“

Ich selbst habe bisher nur ein Fragment geschrieben, welches sich nur die auf das Suchen einer bestimmten Zeichenkette in der „FE-Simulation.k“ und Ersetzen dieser durch eine andere festgelegte Zeichenkette konzentrieren soll. Leider funktioniert das gar nicht.
Weiterhin frage ich mich wie ich diese iterative Substitution implementieren soll. Meine Überlegung basiert auf einer If_else if_else-Abfrage, habe jedoch bisher keinen Ansatz wie die einzelnen (Abruch-) Kriterien lauten könnten.

Ich wäre äußerst dankbar, falls jemand mir konkret weiterhelfen könnte oder mich auf Threads verweist, die ähnliche Probleme behandeln. Hier mein bisheriges Textfragment:

Code: Alles auswählen

import fileinput, sys
fobj_in = open("FE-Simulation.k", "r") 
search_text = "  0.100000     0.000     0.000     0.000     0.000         0     0.0001.0000E+20"
replace_text = "  0.200000     0.000     0.000     0.000     0.000         0     0.0001.0000E+20"
for line in fileinput.input("FE-Simulation.k",inplace=504): // 504 ist die Zeile in der die Substitution erfolgen soll
	line = line.replace(search_text, replace_text)
sys.stdout.write(line)
fobj.close()
BlackJack

@Ambig: Du musst das Problem in kleinere Teilprobleme zerlegen und die dann einzeln mit Funktionen lösen. Wobei man auch Teilprobleme ruhig noch mal zerlegen kann. Solange bis ein Teilproblem so klein geworden ist, dass man es mit einer übersichtlichen Funktion die genau eine abgeschlossene Sache erledigt, lösen kann. Das testet man dann und wenn es funktioniert geht man zum nächsten Teilproblem über.

Das mit dem Testen ist wichtig und auch das man nicht neuen Code schreibt solange der bisherige nicht richtig funktioniert. Der gezeigte Quelltext ist zum Beispiel so ziemlich komplett falsch. Der kompiliert nicht mal weil ``//`` in Python ein mathematischer Operator und kein Kommentar ist und der an der Stelle syntaktisch falsch ist. Bei `fileinput.input()` sind beide Argumente vom Wert her falsch. Das erste hätte eine Sequenz von Dateinamen sein müssen, was es formal ist weil Zeichenketten eine Sequenz von Zeichenketten mit jeweils einem Buchstaben sind, aber das ist hier ja sicher nicht gewollt das versucht wird erst die Datei 'F' dann die Datei 'E', dann die Datei '-', und so weiter, zu verarbeiten. Und `inplace` ist ein Flag. Da sind laut Dokumentation die Werte 0 und 1 sinnvoll. Schönes Beispiel warum man in neuem Code `True` und `False` statt 1 und 0 nehmen sollte, dann kommt so schnell keiner auf die Idee man könnte dort auch 504 angeben. Und am Ende wird versucht `fobj` zu schliessen, der Name wird aber vorher gar nicht definiert.

Am besten öffnet man Dateien zusammen mit der ``with``-Anweisung, dann wird sie auf jeden Fall geschlossen wenn der Programmfluss den ``with``-Block verlässt.

Das mit der fixen Zeilennummer klingt nicht besonders robust. Insbesondere wenn die so hoch ist wie im Beispiel, gibt es ja doch eine gewisse Gefahr dass sich irgendetwas davor in der Datei mal ändert und sich die gesuchte Zeile verschiebt. Kann man die Zeile nicht irgendwie anders identifizieren? Das gleiche gilt für die Hilfsdatei.
Ambig
User
Beiträge: 2
Registriert: Dienstag 14. April 2015, 08:38

Vielen Dank für die schnelle Antwort!

ich habe den Code jetzt wie folgt aufgebaut:

Code: Alles auswählen

import fileinput, sys
with open("FESimulation.py", "r") as mySim:
    search_text = "Spotweld"
    replace_text = "  0.200000     0.000     0.000     0.000     0.000         0     0.0001.0000E+20"
    for line in fileinput.input("FESimulation.py", inplace = 0):
        line = line.replace(search_text, replace_text)
        sys.stdout.write(line)
Wenn ich den Code so in der Shell laufen lasse wird die replace-Funktion ordentlich geprintet. Ich möchte jedoch, der String "Spotweld" den entsprechenden replace-String ersetzt in der FE-Simulations.py-Datei überschrieben wird. Dafür habe ich versucht das Argument in open() anstatt "r" durch "r+" oder "w" oder "w+" zu ersetzen. Dann wird jedoch der gesamte Quelltext der Datei gelöscht. Liegt das daran, dass ich das falsche Argument verwende oder hat das einen anderen Grund?

Das erste Argument von input() wird mir nicht als falsch angezeigt bzw. als Versuch die einzelnen Dateien F, E, - etc. aufzurufen. Beim Weglassen der "" wird stattdessen gesagt, dass der Dateiname nicht definiert ist.

Weiterhin stimme ich dir zu, dass das Suchen nach fixen lines nicht wirklich robust ist. Stattdessen müsste man im ersten Durchgang nach dem zu ersetzenden string suchen, der einzigartig im Quelltext vorkommt, und im zweiten Durchgang den ersetzen string suchen der ebenfalls einzigartig ist im Quelltext.
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ambig: Du kannst in Text-Dateien nichts ersetzen. Du änderst ja die Länge einer Zeile und dann müßte nachfolgend der ganze Inhalt der Datei verschoben werden, was aber nicht automatisch passiert. Der robuste Weg ist es, alles in eine neue Datei zu schreiben und gegebenenfalls die Datei am Ende in den Namen der Ursprungsdatei umzubenennen.
BlackJack

@Ambig: Man kann Text in Textdateien im allgemeinen nicht „in place” ersetzen. Das übliche Vorgehen ist deshalb die geänderten Daten in eine neue, temporäre Datei zu schreiben und diese dann am Ende zur originalen Datei umzubenennen. Das hat auch den Vorteil das die originale Datei unverändert vorhanden bleibt bis der Vorgang komplett durchgelaufen ist, also insbesondere auch dann wenn der Vorgang *nicht* durchläuft, zum Beispiel weil er mit einer Fehlermeldung abgebrochen wird.

Ich verstehe nicht so ganz was das `fileinput.input()` in dem Quelltext jetzt noch zu suchen hat‽ Du hast die Datei bereits geöffnet und das Dateiobjekt an den Namen `mySim` gebunden, mit dem dann nichts gemacht wird.

`fileinput.input()` prüft anscheinend ob man statt eines iterierbaren Objekts für `files` nur eine einzelne Zeichenkette angibt. Aber es bleibt auch dann IMHO das falsche Werkzeug weil Du nichts davon benutzt. Das bereits im Code vorhandene `open()` reicht aus.

Wenn ab dem ersten Durchgang der zu suchende Wert der Zeile kein eindeutiger Bezeichner sondern eine Zeile mit Zahlenwerten ist, dann ist das aber auch nur für den ersten Durchgang wirklich robust. Ich würde mir ja eher merken welche Zeilennummer im ersten Durchgang gefunden wurde. Oder den Punkt in der Datei markieren, zum Beispiel mit einem speziellen Kommentar nach dem man suchen kann, falls das Dateiformat Kommentare erlaubt.

Um enstscheiden zu können wie man den Gesamtablauf gestaltet wäre es auch nicht unwichtig zu wissen wie die Datei überhaupt zustande kommt. Eventuell geht das ja noch einfacher in dem man einfach eine Templating-Enginge verwendet — im einfachten Fall `string.Template` aus der Standardbibliothek. Oder wenn man gleich von Anfang an, also vor dem Programm per Hand, so einen speziellen Kommentar in die Datei einfügen kann (was ja letztendlich auch eine Art von Templating wäre). Und läuft das Programm das die Ersetzung durchführen soll ständig oder wird das für jeden Ersetzungsschritt extra aufgerufen?
Antworten