Datei bearbeiten

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.
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Hallo zusammen,

kurz zu mir, ich bin 35 Jahre alt und befasse mich erst seit kurzem mit Python bzw. überhaupt mit dem Programmieren. Grund dessen ist, dasss ich in meiner Freizeit und auch bei der Arbeit mich mit Simulationen beschäftige und nun gerne bestimmte Verfahren automatisieren möchte. Kaum fange ich an tritt schon meine Unwissenheit in Python hervor. Der Einstieg klappt eigentlich ganz gut.

Zu meinem Problem:
Ich habe eine m4 macro Datei die einen C source code enthält (30kb Größe.) Indieser möchte ich Zeilen kopieren und zwischen anderen Zeilen einfügen
Bsp.:

1 TextZeile1
2 TextZeile2
3 kein Text
4 TextZeile4
5 TextZeile5

Nun möchte ich den Text aus Zeile 1 und 2 zwischen Zeile 4 und 5 mindestens 2 mal einfügen und den rest dabei nicht überschreiben. Nachdem was ich aus Tutorials und dem Forum erfahren habe, kann man Dateien einlesen r und auch Dateien schreiben w. Ich verstehe jedoch nicht den zugriff auf mehrere Zeilen und diese dann anschließend in eine bestimmte Zeile merhmals zu kopieren.

Es eilt nicht, da ich dies momentan erst nur in meiner Freizeit mache, aber ich würde mich freuen wenn jemand mir anhand dieses Beispiels helfen könnte.

Mit freundlichen Grüßen
Thomas
BlackJack

@thomas79: In Dateien kann man mit der Datei-API die einem aktuelle Betriebssysteme anbieten nichts einfügen oder löschen ohne entweder Lücken mit ”Müll” zu hinterlassen oder vorhandene Daten zu überschreiben. Man kann höchstens am Ende etwas anfügen oder löschen. Bei Deinem Vorhaben musst Du also entweder die Datei komplett in den Speicher lesen und dann dort die Daten manipulieren und die Datei mit den veränderten Daten komplett neu beschreiben, oder Du musst eine temporäre Datei öffnen und die Daten dann kopieren und ggf. dabei die Veränderungen vornehmen. Am Ende kannst Du die temporäre Datei zur ursprünglichen umbennenen. Das hat den zusätzlichen Vorteil dass Du die ganze Zeit immer noch die Originaldatei hast bist kurz bevor die fertige neue Datei diese ersetzt. Wenn das Programm also aus irgend welchen Gründen mittendrin abbricht (Fehler, Benutzer bricht ab, …) gehen keine Daten verloren.

Wenn Du Dich wirklich an Zeilennummer orientierst könntest Du für Dein Beispiel erst die beiden ersten Zeilen in den Speicher einlesen, die in die Zieldatei schreiben, dann zwei Zeilen von der Quell in die Zieldatei kopieren, dann die ersten beiden Zeilen nochmal so oft wie Du das haben möchtest in die Zieldatei schreiben, und danach den Rest kopieren. Dazu bietet es sich an sich mit Iteratoren, Generatoren, iterierbaren Objekten im allgemeinen, und dem Inhalt vom `itertools`-Modul vertraut zu machen.
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Danke dir BlackJack,

dein Vorschlag klingt hervorragend. Ich werde mich mal in den genannten Themen einlesen und mich dann bei Porblemen oder einem Erfolg nochmal melden.

Vielen Dank
Thomas
BlackJack

Mal komplett mit Mitteln aus `itertools` ohne explizite Schleifen ausgedrückt:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function

from itertools import chain, islice, repeat


def main():
    with open('test.txt', 'r') as lines:
        with open('test2.txt', 'w') as out_file:
            first_lines = list(islice(lines, 0, 2))
            out_file.writelines(
                chain(
                    first_lines,
                    islice(lines, 0, 2),
                    chain.from_iterable(repeat(first_lines, 2)),
                    lines
                )
            )


if __name__ == '__main__':
    main()
Inhalt der beiden Textdateien:

Code: Alles auswählen

bj@dubnium:~$ cat test.txt 
1 TextZeile1
2 TextZeile2
3 kein Text
4 TextZeile4
5 TextZeile5
bj@dubnium:~$ cat test2.txt 
1 TextZeile1
2 TextZeile2
3 kein Text
4 TextZeile4
1 TextZeile1
2 TextZeile2
1 TextZeile1
2 TextZeile2
5 TextZeile5
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Danke dir BlackJack für den Code.

Ich verusche den Code gerade mit meinem bisherigen Wissen zu verstehen:
- Zunächst wird die Datei test.text im Generator main zeilenweise eingelesen.
- Dann wird eine zweite Datei test2.txt im Modus w (write) erstellt.
- Unter first_lines wird eine Liste aus den eingelesenen Zeilen definiert. Diese enthält die Zeilen 0 und 1, da bei 2 ja gestopt wird.
- chain (hier werden nun 4 verschiedene Iteratoren verkettet)

- Zeile 0 und 1 wird geschrieben
- islice(lines, 0, 2),? erneut Zeile 0 bis 1?
- chain.from_iterable(repeat(first_lines, 2)), hier werden die Zeilen 0 bis 1 2mal wiederholt
- lines schreibt demnach die letzten Zeilen nieder

if __name__ == '__main__':
main()

Bei den letzten beiden Zeilen aus dem Code sowie in chain () ist mein Verständnis noch lückenhaft, die Thematik so schnell zu verstehen ist doch recht hartnäckig.
Ich verstehe daher nicht wann
3 kein Text
4 TextZeile4
in das file geschrieben wird.

schönen Feierabend
Thomas
BlackJack

@thomas79: `main()` ist kein Generator sondern eine ganz normale Funktion. Die Datei wird in der ``with``-Zeile auch nicht eingelesen sondern nur geöffnet und `lines` ist ein iterierbares Objekt über die einzelnen Zeilen in der Datei.

Bis zum `chain()` mit 4 Iteratoren stimmt's und dort auch der erste Punkt. Beim zweiten ``islice(lines, 0, 2)`` werden die nächsten zwei Zeilen aus `lines` als Iterator geliefert. Die ersten beiden wurden ja schon in die Liste `first_lines` eingelesen. Womit Deine letzte Frage beantwortet sein sollte: Das sind die Zeilen 3 und 4 aus der Eingabedatei.

Das ``if __name__ == '__main__':``-Idiom sorgt dafür das man die Datei als Programm ausführen kann, aber auch als Modul importieren kann *ohne* dass das Hauptprogramm abläuft. `__name__` wird nämlich normalerweise an den Namen des Moduls gebunden. Ausser man startet es als Programm, dann hat `__name__` den speziellen Wert '__main__'. Bei dem kleinen Programm hat das noch keinen wirklichen Vorteil, aber sowie die Programme etwas umfangreicher werden und man den Code auf Funktionen (und Klassen) aufteilt ist es praktisch das man das Modul zum Beispiel in einer Python-Shell importieren kann und einzelne Funktionen aufrufen kann, beispielsweise um sie zu testen oder Fehler einzugrenzen. Und man kann automatisierte Tests schreiben, Funtkionalität in anderen Modulen wiederverwenden, und so weiter.
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Danke dir erneut BlackJack,
habe mir hinsichtlich der Thematik nochmal ein paar Dinge durchgelesen und hab den Code variieren und nachbauen können.
Ein letztes Problem habe ich jedoch noch, der mir vorher nicht aufgefallen zu sein scheint, dann sollte ich das ganze automatisiert haben.

Sagen wir bei den Zeilen 1 und 2 ist nun jeweils ein Parameter enthalten
1 TextZeile1 Parameter i
2 TextZeile2 Parameter j

Wenn ich un die Zeilen 1 und 2 zwischen 4 und 5 mehrmals kopiere, wäre es am Besten, dass bei jedem Kopiervorgang sich der Parameter1 und der Parameter 2 jeweils um 1 erhöht.
Nach meinem bisherigen Wissen würde ich eine Zählschleife einbauen, die den Kopiervorgang enthält und den jeweiligen Parameter einsetzt und anschließend um 1 erhöht.

Hoffe man kann dies noch auf irgendeine Möglichkeit dort einfügen und der ganze Code ist nicht hinfällig.

Viele Grüße & einen guten Abend
Thomas
BlackJack

@thomas79: Hast Du eigentlich Einfluss auf diese Datei? Also kannst Du die selber schreiben beziehungsweise von Hand verändern? Denn so langsam kommt glaube ich auch ein Template-System wie jinja2 oder etwas ähnliches in Frage.

Wenn Du die Datei so nehmen musst wie sie kommt, stellt sich die Frage wie man den veränderlichen Teil innerhalb einer Zeile identifiziert.
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

@BlackJack: die komplette Datei wurde von mir selbst entwickelt und lässt sich zu 100% verändern.

- jinja2, noch etwas von dem ich noch keine Ahnung habe. Kennst Du dich damit gut aus beziehungsweise hast Du eine Vorstellung wie sich meine Problematik damit umsetzen lässt?
BlackJack

@thomas79: Ich benutze die Bibliothek für Webanwendungen als Template-Engine.

Beispiel-Template:

Code: Alles auswählen

{%- set i = 42 -%}
{%- set j = 23 -%}
{%- macro first_lines(i, j) -%}
1 TextZeile1 Parameter {{i}}
2 TextZeile2 Parameter {{j}}
{%- endmacro -%}
{{ first_lines(i, j) }}
3 kein Text
4 TextZeile4
{% for k in range(1, 3) -%}
{{ first_lines(i + k, j + k) }}
{% endfor -%}
5 TextZeile5
Python-Quelltext:

Code: Alles auswählen

from __future__ import absolute_import, division, print_function
from jinja2 import Environment, FileSystemLoader


def main():
    environment = Environment(loader=FileSystemLoader('.'))
    template = environment.get_template('test.txt')
    print(template.render())


if __name__ == '__main__':
    main()
Ausgabe:

Code: Alles auswählen

1 TextZeile1 Parameter 42
2 TextZeile2 Parameter 23
3 kein Text
4 TextZeile4
1 TextZeile1 Parameter 43
2 TextZeile2 Parameter 24
1 TextZeile1 Parameter 44
2 TextZeile2 Parameter 25
5 TextZeile5
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Bin aktuell bei der Arbeit. Dort dauert es ein wenig bis ich mir Jinja2 installieren lassen kann. Klingt aber an sich schonmal super und die Lösung sieht auch nachdem aus was ich mir vorstelle. Bis ich wieder an meinem privaten Rechner sitze versuche ich das Problem mit den Parametern mit dem vorherigen Ansatz (itertools) hinzubekommen. Würde die Lösung mit jinja2 auch gerne ohne jinja 2 hinbekommen. Dann habe ich zwei neue Ansätze kennengelernt, die ich hinsichtlich der Problematik mit den Parametern anwenden kann.
Eventuell hat jemand auch dazu noch eine Lösung.

Viele Grüße
Thomas
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Ok, merke, dass das mit Jinja2 einfach alles besser und einfacher funktioniert. Splitte meine Datei nun in einzelne Templates auf. Die Bitte vom Post vorher hat sich somit erledigt.
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Ich habe nun ein Base-Template erstellt, welches mehrere Verweise auf Child-Templates beinhaltet. Nun habe ich sowohl im Base Template als auch im Child Templates die Parameter geo_metry.i und geo_metry.j verwendet. Bis dato ganz in Ordnung. Welche Werte die Parameter besitzten soll nun aber nicht im Template, sondern eher im Python Quelltext stehen:

Code: Alles auswählen

from __future__ import absolute_import, division, print_function
from jinja2 import Environment, FileSystemLoader

class Geometry():
  def __init__(self):
    self.i
    self.j
    return

if __name__ == "__main__":
  geometry = Geometry(5,4)
  environment = Environment(loader=FileSystemLoader('.'))
  template = environment.get_template('test.txt')
  print(template.render('test', geometry=geo_metry))
Doch es funktioniert einfach nicht, muss ich diese Child Templates auch noch laden?
Bzw. wie bekomme ich das hin?
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@thomas79: das funktioniert nicht, weil Du einen NameError bekommst, dass geo_metry nicht definiert ist. Funktioniert nicht ist auch eine schlechte Fehlerbeschreibung. Gibt es eine Fehlermeldung? Wie sehen die Templates aus? Was ist das Ergebnis? Und wie sollte es aussehen?
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Ok, ich versuche mal anderes anzufangen. Im Grund habe ich das Template von oben genommen und es um ein Child_Template erweitert. In dem Child_Template habe ich einen Parameter holz eingefügt, welcher im Python Skript (auch aus dem vorherigen Beispiel) mit 2000 definiert wurde. Schreibe ich den Parameter holz ohne Verwendung des Child_Templates in das Base_Template funktioniert alles und in der letzten Zeile wird 2000 ausgegeben. Wenn ich nun aber das Child_Template verwende, dann funktioniert dies nicht. Ich vermute ich muss im Python-Skript noch etwas hinzufügen, damit man weiß, dass neben dem Base_Template auch das Child_Template vorhanden ist. Jedoch weiß ich noch nicht wie.

Base_Template:

Code: Alles auswählen

{%- set i = 42 -%}
{%- set j = 23 -%}
{%- macro first_lines(i, j) -%}
1 TextZeile1 Parameter {{i}}
2 TextZeile2 Parameter {{j}}
{%- endmacro -%}
{{ first_lines(i, j) }}
{% block Child_Template %}
{% endblock %}
Child_Template:

Code: Alles auswählen

{% extends "Base_Template" %}
{% block title %}
{{holz}}
{% endblock %}
Python-Quelltext:

Code: Alles auswählen

from __future__ import absolute_import, division, print_function
from jinja2 import Environment, FileSystemLoader
holz=2000
def main():
    environment = Environment(loader=FileSystemLoader('.'))
    template = environment.get_template('Base_Template')
    print(template.render(holz=holz))

if __name__ == '__main__':
    main()
Benutzeravatar
Kebap
User
Beiträge: 687
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

thomas79 hat geschrieben:Wenn ich nun aber das Child_Template verwende, dann funktioniert dies nicht.
Du wurdest ja schon gebeten, deine Probleme genauer zu beschreiben, als nur "funktioniert nicht".

Deine Vermutungen in allen Ehren, aber hast du vielleicht schon das Tutorial angeschaut, wo erklärt wird, wie das funktioniert? Üblicherweise rät man sowas nicht, sondern liest das einfach durch, und weiß dann wie es geht.
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
BlackJack

@thomas79: Du musst im Skript das Kindtemplate verwenden, das erweitert ja das Basistemplate. Und dann musst Du natürlich im Kindtemplate einen Block überschreiben den es im Basistemplate auch gibt, also zum Beispiel `Child_Template` anstatt `title`.
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Danke BlackJack,

das mit dem title war mir zwar klar, habbe ich jedoch die ganze Zeit überlesen. Aber, dass ich das Kindtemplate angeben muss war der Knackpunkt.

Danke nochmal!
thomas79
User
Beiträge: 12
Registriert: Dienstag 7. Juli 2015, 08:44

Nochmal ich kurz, sry.

Wenn ich nun ein zweites kindtemplate hinzufügen möchte, was muss ich im Python-Skript ändern? Einfach per Komma trennen geht nicht.

Zum Post davor: Ich finde das nicht in der Jinja2-Doku, kann an meinem schlechten Englisch liegen. Ein link würde mir dennoch als Antwort vollkommen reichen.
BlackJack

@thomas79: Mehrfachvererbung ist AFAIK nicht vorgesehen. Würde zumindest bei Webseiten auch anfangen komisch zu werden weil ein Elterntemplate normalerweise die komplette Grundwebseite enthält, also Anfang und Ende enthält und Kinder dann Teile des Inhalts festlegen. Es gibt aber auch noch ``include`` und ``import`` die man benutzen kann um ausgefüllte Templates einzufügen oder Macrosammlungen wiederzuverwenden.
Antworten