Automatisches Auslesen von Dateien und Erstellen einer Neuen

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.
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

Hallo Python Forum,

dies ist mein erster Beitrag und schon komme ich mit so einer komischen Frage.
Nun ja, also mein Problem ist folgendes:
Ich habe mehrere .cfg Dateien, die aber nicht der config Formatierung vom Python ConfigParser entsprechen, also ohne [sections] und dafür mit geschweiften und runden Klammern. In etwa so:

Code: Alles auswählen

threads:
{
dmathreads = ( 

	{ 
	  id=0;  			# ThreadID (just for display)
          threadname="Worker1"; 	# Threadname (just for display)
         ...
        } );

Die Dateien beinhalten noch mehr von diesem Kram, aber primär ist für mich das Stichwort "threadname" wichtig.
Ich kann diese Dateien natürlich mit

Code: Alles auswählen

string1 = open(...cfg).read()
öffnen und in einen string schreiben. Außerdem danach mit

Code: Alles auswählen

if "threadname" in string1: print 'true'
nach dem Wort suchen.
Ich konnte nur bisher nicht herausfinden, wie ich den Begriff, der "threadname" zugeordnet wird, mir ausgeben lassen kann. Ich habe schon Stunden gesucht und konnte dazu nichts finden.

Leider kann ich auch nicht mit dem ConfigParser arbeiten, da dieser das spezielle Format verlangt. Ich darf jedoch die .cfg Dateien in ihrem Inhalt nicht ändern.
Ich schätze, dass es eine Funktion gibt, mit der ich mir das zugewiesene Wort ausgeben lassen kann. Der Punkt ist, dass ich mehrere dieser Dateien mit variablen Namen und variablen Zuweisungen (also auch variable Stringlänge) automatisch auslesen will, ohne eben die Namen und länge der Dateien und Zuweisungen zu kennen. Leider bin ich bisher nicht besonders weit gekommen, wie man sehen kann.
Mein Code bisher ist dadurch quasi nur:

Code: Alles auswählen

if 'threadname' in open(...cfg).read(): print 'true'
Wenn ich diese Dateien ausgelesen habe, möchte ich danach automatisch eine Latex Datei erzeugen, die die gefilterten Informationen mit einem bestimmten Kontext enthält. Aber das ist erstmal nebensächlich und es wäre schon eine riesen Hilfe, wenn der erste Teil erstmal steht.
Ich hoffe ihr könnt mir weiterhelfen, da Python für mich neu ist. Dennoch habe ich auch im Manual und im Forum leider vergeblich nach Lösungen gesucht und weiß jetzt nicht mehr weiter. Es ist natürlich möglich, dass ich durch Unwissenheit hilfreiche Beiträge nicht als solche erachtet habe.

Vielen Dank im Voraus,
Humpalumpa
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Was für ein Format ist das denn? Wenn es ein gängiges ist, gibt es evtl. schon einen Parser in Python, den man nutzen könnte. Wenn nicht (und Du das Format nicht ändern kannst), musst Du einen eigenen bauen; genau das, was Du letztlich schon versuchst.

Da gibt es mehrere Möglichkeiten; je nach Anwendungsfall sind die unterschiedlich geeignet. Das einfachste Mittel stellen die String-Funktionen von Python dar, danach kämen als Ergänzung reguläre Ausdrücke hinzu und als letztes spezielle Module für das Bauen von Parsern.

Wenn es Dir wirklich nur um die Zeile ``threadname="Worker1";`` geht, kann man das einfach mittels ``str.split`` umsetzen. Schau Dir das mal in der offiziellen Doku an und probiere dann in einer Python-Shell, wie man die Methode sinnvoll einsetzen kann. Ach ja, Indexzugriffe sollte man natürlich dafür kennen - also wirklich nur Basics.

Dateien solltest Du übrigens mittels ``with open(...) as handler_name`` öffnen - damit sparst Du Dir ein explizites ``handler_name.close()``, um die Datei zu schließen und es klappt auch im Fehlerfall garantiert.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Humpalumpa
Wenn die Zeile nicht nur in etwa sondern tatsächlich so aussieht, könntest Du die Methode ``split()`` verwenden:

Code: Alles auswählen

>>> 'threadname="Worker"'.split('=')
['threadname', '"Worker"']
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

Danke für die schnelle Antwort.

@Hyperion: Vielen Dank für die ausführliche Antwort, ich werden mir die Split Funktion mal ansehen. Leider finde ich es ziemlich schwierig am Anfang gut voran zu kommen, da man einfach die Bandbreite der ganzen Funktionen noch nicht kennt.
Die Dateien sind als .cfg formatiert, jedoch mit einem eigenen Parser geschrieben, auf den ich keinen Zugriff habe. Aber glücklicherweise funktioniert die einfache "open" Funktion (bzw. mit helper).

@mutella: Mein Problem ist, dass ich nur das Stichwort "threadname" kenne, jedoch nicht den zugewiesenen Namen.


Vermutlich kann man mit der standart Bibliothek "re" etwas anfangen.
Also nachdem ich hier http://openbook.galileocomputing.de/pyt ... 15_002.htm etwas genauer nachgelesen habe, sieht es so aus als könne man in etwa mit

Code: Alles auswählen

import re
 r"treadname="(*)""
etwas anfangen. Leider scheint die Syntax so nicht ganz zu funktionieren.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Humpalumpa
Warum eigentlich nicht gleich ein regex? Damit würdest Du Dir auch das Suchen nach der passenden 'threadname=...' Zeile sparen:

Code: Alles auswählen

>>> import re
>>> re.findall(r'threadname="(.*)"', 'threadname="Worker";')
['Worker']
Der reguläre Ausdruck bedeutet: Suche eine Gruppe von Buchstaben beliebiger Anzahl, die innerhalb eines Strings 'threadname="' und einem '"' steht.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

@mutetella

Die findall Funktion scheint ganz gut zu sein, leider muss man dort scheinbar auch den genauen Namen (in diesem Fall "Worker") angeben. Ich brauche jedoch ein Programm, was diesen Namen automatisch herausfindet, nur indem er die Zuweisung und die vorherigen, sowie nachfolgenden Zeichengrenzen kennt (also nur threadname="(hier unbekanntes Wort)").

Mit

Code: Alles auswählen

re.findall(r'threadname="(.*)"', 'threadname="Worker";')
gibt er mir ja nur das aus, was ich im zweiten Segment der findall Funktion angebe. Hast du eine Idee, wie das zu lösen ist?

Ich hätte gern in etwa sowas:

Code: Alles auswählen

re.findall(r'threadname="(.*)"') 
[Worker] 
Als Ausgabe dann eben gesuchten threadname.

Bei der str.split() Funtkion kann ich doch auch nur nach bekannten strings suchen, oder hab ich die Falsch verstanden?
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Anscheinend hast du dir die Funktion noch nicht wirklich angeschaut.

Hier mal ein Test.

Code: Alles auswählen

>>> import re
>>> re.findall(r'threadname="(.*)"', 'threadname="Worker";')
['Worker']
>>> re.findall(r'threadname="(.*)"', 'threadname="Server";')
['Server']
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Humpalumpa hat geschrieben:Ich hätte gern in etwa sowas:

Code: Alles auswählen

    re.findall(r'threadname="(.*)"')
    [Worker] 
Ehrlich gesagt verstehe ich nicht wirklich, was Du denn möchtest? Es schaut so aus, als würdest Du ein `Worker` Objekt als Rückgabe benötigen? Dafür müsstest Du aus dem String 'Worker' oder was immer auch als Ergebnis da ist, ein Objekt erstellen. `getattr()` fällt mir dazu ein oder ein Mapping, das Dir ein gewünschtes Objekt zurückgibt.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

mutetella hat geschrieben: Ehrlich gesagt verstehe ich nicht wirklich, was Du denn möchtest? Es schaut so aus, als würdest Du ein `Worker` Objekt als Rückgabe benötigen? Dafür müsstest Du aus dem String 'Worker' oder was immer auch als Ergebnis da ist, ein Objekt erstellen. `getattr()` fällt mir dazu ein oder ein Mapping, das Dir ein gewünschtes Objekt zurückgibt.

mutetella
Hi mutetella,
danke für deine tatkräftige Unterstützung. Scheinbar habe ich mich falsch ausgedrückt. Tut mir leid, wenn ich bei euch verwirrung gestiftet habe. :oops:
Ich brauche tatsächlich nur den Rückgabewert, der "threadname=" zugewiesen wird.
Ich schaue mir mal "getattr()" an. Vielen Dank schomal.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Humpalumpa
Einfach mal noch als Anregung, weil ich tatsächlich noch nicht wirklich weiß, was Du möchtest...:

Code: Alles auswählen

>>> class Worker(object):
...     ANY_ATTR = 'any attr'
... 
>>> class Server(object):
...     pass
... 
>>> objects = {'Worker': Worker, 'Server': Server}
>>> w = objects['Worker']
>>> w
<class '__main__.Worker'>
>>> getattr(w, 'ANY_ATTR')
'any attr'
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

@mutetella

Okay, ich glaube ich habs wirklich schlecht erklärt. Sorry :cry:

Ein neuer strukturierter Versuch:

Ich habe mehrere Dateien, wie in etwa:
  • datei1.cfg
  • datei2.cfg
  • datei3.cfg


Diese sind vom Inhalt unterschiedlich, haben aber alle eine Zuweisung mittels:

Code: Alles auswählen

threadname="xxx"
Dabei steht xxx für den unbekannten Zuweisungsnamen, der bei jeder Datei anders ist.
Dieser kann sein "Worker1", "cpurun", etc.

Ich kenne den Inhalt von xxx nicht, will ihn nun aber herauslesen und in eine neue Datei schreiben.
Und das bei einer unbekannten Anzahl von Dateien. (Dies lässt sich dann aber über eine Schleife realisieren.)

Mein primäres Problem ist, dass ich nur die Grenzen, in denen xxx liegt kenne, jedoch nicht den Inhalt.
Deshalb, um das Programm auf alle Dateien anwenden zu können, kann ich keinen konkreten Suchinhalt angeben, sondern nur den Bereich, indem er liegt (eben genanntes [threadname=" "]).

Ich hoffe, dass das jetzt irgendwie verständlich und nicht noch verwirrender ist. :)
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Humpalumpa
Ok, dann hast Du ja jetzt alle Werkzeuge zur Hand, die Du benötigst. Damit gehst Du nun Zeile für Zeile jeder Datei durch (``for line in file:``) und ziehst die gewünschten Strings heraus.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

/me hat geschrieben:Anscheinend hast du dir die Funktion noch nicht wirklich angeschaut.

Hier mal ein Test.

Code: Alles auswählen

>>> import re
>>> re.findall(r'threadname="(.*)"', 'threadname="Worker";')
['Worker']
>>> re.findall(r'threadname="(.*)"', 'threadname="Server";')
['Server']
Hallo /me,
doch ich habe sie genau so verstanden, wie dein Beispiel zeigt. Das Problem ist, dass ich eben den zugewiesenen String von threadname nicht kenne.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Humpalumpa hat geschrieben:Das Problem ist, dass ich eben den zugewiesenen String von threadname nicht kenne.
Musst Du ja auch nicht. Den liefert Dir `findall()` ja. Und dann hast Du ihn! :wink:

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

mutetella hat geschrieben:@Humpalumpa
Ok, dann hast Du ja jetzt alle Werkzeuge zur Hand, die Du benötigst. Damit gehst Du nun Zeile für Zeile jeder Datei durch (``for line in file:``) und ziehst die gewünschten Strings heraus.

mutetella
Okay mutetella, scheinbar bist du schlauer als ich. Ich kann gerade keine sinnvolle Verbindung ziehen.
Kannst du mir evtl ein konkretes Beispiel geben? Sorry, wenn das nervig erscheinen sollte, aber ich weiß im Moment nicht, wie ich "for line in file" nutzen kann. :K

Ich habe es so probiert:

Code: Alles auswählen

string1 = 'threadname=""' for line in file:'datei1.cfg'
Was leider nicht funktioniert, weil vermutlich etwas fehlt oder syntaktisch falsch ist.


Ich kann mittels re.findall doch nur konkrete Strings suchen oder?
Es funktioniert ja schließlich nicht, wenn ich nur

Code: Alles auswählen

re.findall(r'threadname="(.*)"')
eingebe.

Denn

Code: Alles auswählen

re.findall(r'threadname="(.*)"','threadname="Worker"')
kann ich nicht nutzen, da ich den string "Worker" nicht kenne und er je nach Datei anders heißt.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Humpalumpa
Uiuiui, da fehlen Dir aber doch ein paar Basics. Ich empfehle Dir, einmal das offizielle Python Tutorial durchzuarbeiten...
Humpalumpa hat geschrieben:Ich kann mittels re.findall doch nur konkrete Strings suchen oder?
Na eben nicht, denn hättest Du einen "konkreten String", dann müsstest Du ihn ja nicht suchen, oder? Ein regulärer Ausdruck ist letztlich ein Schema, das auf einen String angewendet wird. Das Ergebnis ist dann all das, das auf das Schema passt.
Die Funktion `findall()` erwartet 2 Argumente. Erstens den regulären Ausdruck, also das Schema. Zweitens den String, der durchsucht werden soll. In unseren Beispielen haben wir ja nur der Einfachheit halber dort einen "konkreten String" angegeben. Natürlich wird später einmal an dessen Stelle ein Name stehen, in etwa sowas:

Code: Alles auswählen

with open(filename, 'r') as file:
    for line in file:
        result = re.findall(r'threadname="(.*)"', line)
        ...
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@Humpalumpa: um das ganze abzukürzen:

Code: Alles auswählen

with open('datei.cfg') as cfg:
    threadnames = re.findall('threadname="(.*?)"', cfg.read())
Und jetzt finde den Unterschied zu Deinen Versuchen.
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

mutetella hat geschrieben: Die Funktion `findall()` erwartet 2 Argumente. Erstens den regulären Ausdruck, also das Schema. Zweitens den String, der durchsucht werden soll. In unseren Beispielen haben wir ja nur der Einfachheit halber dort einen "konkreten String" angegeben. Natürlich wird später einmal an dessen Stelle ein Name stehen, in etwa sowas:

Code: Alles auswählen

with open(filename, 'r') as file:
    for line in file:
        result = re.findall(r'threadname="(.*)"', line)
        ...
mutetella
Ahaa, jetzt verstehe ich. Tut mir leid, das hat lange gedauert, ich habe jetzt auch das richtige schonmal rausbekommen. Vielen vielen Dank! :D

Ja ich bin noch ein ziemlicher Newbie im Programmieren und Python hab ich vor 5 Jahren in der Schule gehabt. Jetzt komme ich nicht drum herum, lerne hier aber viel dazu. Das Tutorial hab ich mir auch mal angesehen, aber habe nicht alles gelesen. Mir wurde beigebracht immer nur nach dem zu suchen, was man braucht, anstatt erstmal so einen Berg an Informationen durchzuarbeiten. Ich weiß selbst noch nicht, welcher Ansatz besser ist.

Also noch einmal: Vielen Dank!
Ich werde jetzt erstmal Mittagspause machen und mich dann dran setzen. Ich halte euch auf dem Laufenden und auch, falls woanders noch Fragen auftreten sollten. :)
Humpalumpa
User
Beiträge: 9
Registriert: Montag 8. September 2014, 08:04

Hallo nochmal,

wie bereits gesagt, habt ihr mir sehr geholfen.
Ich habe nun quasi das umgekehrte Problem.

Ich kann nun die einzelnen Wörter aus den Dateien auslesen, will nun aber erstmal die gewünschten Dateien auswählen.
Dazu gebe ich einen Pfad ein und lasse mir mittels

Code: Alles auswählen

os.listdir(directory)
die vorhandenen Dateien anzeigen. Dort sind nun aber auch andere Dateien drin und nicht nur .cfg.
Ich habe jetzt versucht mittels:

Code: Alles auswählen

inventory = os.listdir(directory)
	inventstr = ' '.join(inventory)
	
	print 'Es werden folgende Dateien ausgelesen: ', re.findall(r'(.*?).cfg', inventstr)
die Dateien zu separieren, um danach auf sie zugreifen und auslesen zu können.
Leider funktioniert dies nur mit der .cfg Datei, die an erster Stelle im Ordner kommt, durch die r'()' Syntax. Mir ist bisher keine Lösung eingefallen und ich habe schon einiges probiert. Ich hatte gehofft, durch das Fragezeichen diese lange Kette vermeiden zu können, ist jedoch nicht ganz gelungen.
Die Liste 'inventory' kann ich so scheinbar nicht auslesen. Deshalb konvertiere ich sie in einen string mittels ' '.join(inventory). Dabei dachte ich, dass es wohl sinnvoller ist, wenn zwischen den Dateinamen ein Leerzeichen ist, deshalb beim join das Leerzeichen.
Das klappt aber auch nicht so gut, da dann folgendes raus kommt:

Code: Alles auswählen

['datei1', ' desktop.ini bilder test.txt xdatei2']
Datei1 ist korrekt, aber XDatei2 eben nicht. Ich weiß, dass es an der r'()' Abfrage liegt, weiß aber nicht, wie ich diese so gestalten kann, dass das gewünschte ergebnis dabei heraus kommt.
Kann mir da jemand helfen?

Humpalumpa
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Die Lösung ist auch wieder die selbe wie vorher. Benutze eine for-Schleife über "inventory" und teste dort die Endung. Der Umgang mit so elementaren Datenstrukturen gehört zu den absoluten Grundlagen, dein Ansatz sieht mehr nach Raten aus. Vielleicht solltest du noch einmal das Tutorial in der Pythondokumentation durcharbeiten.

Ansonsten gibt es auch noch das glob-Modul.
Das Leben ist wie ein Tennisball.
Antworten