Seite 1 von 2

Einlesen einer Datei in ein Dictionary

Verfasst: Mittwoch 7. Juli 2004, 23:16
von Dookie
Hallo zusammen,

ich stand wiedermal vor dem Problem eine Datei, die aus Schlüssel/Werte-Paaren (getrennt durch ein "=") besteht in ein Dictionary einzulesen. Das Spezielle an der Datei ist, daß Werte, in Anführungszeichen eingeschlossen auch über mehrere Zeilen gehen können. Mit ConfigParser und Konsorten ist da kein Staat zu machen! Aber Python hat ja doch die Batterien schon drinn. Mit einer Regular Expression hab ich jetzt was gebastelt, das Ihr euch gerne mal anschaunen könnt.

Code: Alles auswählen

#!/usr/bin/env python

import sys, re

def file_to_dict(name):
    f = file(name)
    c = f.read()
    f.close()
    result = dict([(x[1],x[2].strip('"').strip())
                    for x in re.findall('((\w+)\s*=[ \t\f\v]*(("[^"]*")|(\S*))[^\n]*\n)',c)])
    return result

if len(sys.argv) == 2:
    print file_to_dict(sys.argv[1])
Hier gleich mal eine Beispieldatei:

Code: Alles auswählen

# Dies ist eine Kommentarzeile
option1 = "nix"

    option2 = viel
option3 = "
Hallo
Welt"
option4 = 4711 #mit kommentar dahinter
noch_eine_Option = 0 ; noch ein kommentar
leer = 
abg = "alpha beta gamma"
Das ganze ist auch sehr robust, wie ihr seht machen Leerzeichen und Kommentare sowie Leerzeilen keine Probleme.


Gruß

Dookie

Verfasst: Donnerstag 8. Juli 2004, 07:33
von mawe
Hi Dookie!

Gefällt mir! :D

Code: Alles auswählen

 print file_dict(sys.argv[1])
Da gehört print file_to_dict(sys.argv[1]) hin.

Hab mir überlegt, daß man das ganze noch flexibler machen könnte, wenn man der Funktion auch das Trennzeichen übergeben kann.

Code: Alles auswählen

import sys, re

def file_to_dict(name,sep):
    f = file(name)
    c = f.read()
    f.close()
    regex = '((\w+)\s*'+sep+'[ \t\f\v]*(("[^"]*")|(\S*))[^\n]*\n)'
    result = dict([(x[1],x[2].strip('"').strip()) for x in re.findall(regex,c)])
    return result

if len(sys.argv) == 2:
    # Trennzeichen soll ein Doppelpunkt sein
    print file_to_dict(sys.argv[1],':')
Jetzt kann man z.B. auch so eine Datei parsen:

Code: Alles auswählen

# Dies ist eine Kommentarzeile
option1 : "nix"

	option2 : viel
option3 : "
Hallo
Welt"
option4 : 4711 #mit kommentar dahinter
noch_eine_option : 0; noch ein kommentar
leer :
abg : "alpha beta gamma"
Gruß, mawe

Verfasst: Donnerstag 8. Juli 2004, 12:32
von Dookie
mawe hat geschrieben:Hi Dookie!

Gefällt mir! :D

Code: Alles auswählen

 print file_dict(sys.argv[1])
Da gehört print file_to_dict(sys.argv[1]) hin.
Hi mawe,

habs geändert.
Das mit dem Seperator ist eine gute Idee.


Gruß

Dookie

Verfasst: Donnerstag 8. Juli 2004, 14:12
von Dookie
kleines update

Code: Alles auswählen

#!/usr/bin/env python

def file_to_dict(name, sep="="):
    f = file(name,'r')
    c = f.read()
    f.close()
    regex = '((\w+)\s*%s[ \t\f\v]*(("[^"]*")|(\S*))[^\n]*\n)' % sep
    result = dict([(x[1],x[2].strip('"').strip()) for x in re.findall(regex,c)])
    return result 

if len(sys.argv) == 2:
    print file_to_dict(sys.argv[1],"[:=]")
Jetzt ist "=" als Trennzeichen voreingestellt, mit "[:=]" z.B. kann sowohl ":" als auch "=" als Trennzeichen fungieren.


Gruß

Dookie

Verfasst: Donnerstag 8. Juli 2004, 17:44
von mawe
Hi!
Dookie hat geschrieben: Jetzt ist "=" als Trennzeichen voreingestellt, mit "[:=]" z.B. kann sowohl ":" als auch "=" als Trennzeichen fungieren.
Das ist natürlich genial, und

Code: Alles auswählen

regex = '((\w+)\s*%s[ \t\f\v]*(("[^"]*")|(\S*))[^\n]*\n)' % sep 
sieht auch eleganter aus als meine Version.

Code: Alles auswählen

def file_dict(name, sep="="): 
...
    print file_to_dict(sys.argv[1],"[:=]")
Dazu sag ich nichts mehr... :wink:

Gruß, mawe

Verfasst: Mittwoch 14. Juli 2004, 02:41
von Dookie
hehe :oops:

jetzt passts aber auch oben mit _to_

und hier noch das Gegenstück:

Code: Alles auswählen

def dict_to_file(d, name, sep='='):
    items = d.items()
    items.sort()
    f = file(name, 'w')
    f.write("# dictionary written by %s\n\n" % sys.argv[0])
    for key, value in items:
        f.write('%s\t%s "%s"\n' % (key, sep, str(value)))
    f.close()
Gruß

Dookie

Verfasst: Montag 27. September 2004, 12:22
von Stolzi
Super das File to Dict Dookie! Danke dafür.
Für mich stellt sich nur noch folgende Frage:
Wenn ich nun keine besondere Datei zum einlesen habe wie du, sondern einfach nur nach einer Funktion suche die mir Einstellungen für mein Programm in einer Datei speichert und wieder ausliest sollte ich dann besser Pickle verwenden? Was würdest du wählen?
Danke
Stolzi

Verfasst: Montag 27. September 2004, 13:11
von Dookie
Hi Stolzi,

ich hab pickle noch nie verwendet :)
Es kommt halt stark auf die Daten an, die du Speichern willst. Bei einfachen Sachen würde ich das File_to_dict, wählen.
Wenn die Daten komplexer und/oder hierarchisch aufgebaut sind (verschachtelte Listen/Dictionaries) würd ich dann zu XML greiffen.
So bleiben die Daten unabhängig, also wenn Du Dein Programm mal z.B. in C umsetzen willst, kannst Du die gleichen Einstellungsdateien verwenden. Ausserdem sind diese Dateien auch von Menschen lesbar und anpassbar.


Gruß

Dookie

Verfasst: Mittwoch 8. Dezember 2004, 22:36
von jens
Hab mir dict_to_file() mal angesehen... Ich fand's doof, das ein Tabulator nicht immer alles auf einer Linie "geordnet" hat... Deshalb wird alles nach dem längsten Key (MaxLen) mittels ljust ausgerichtet:

Code: Alles auswählen

def dict_to_file( Dict, FileName, sep='=' ):
    items = Dict.items()
    items.sort()
    MaxLen = max( [len(key) for key in Dict.keys()] )
    f = file(FileName, 'w')
    f.write("# dictionary written by %s\n\n" % __file__)
    for key, value in items:
        f.write('%s %s "%s"\n' % (key.ljust(MaxLen), sep, str(value)))
    f.close()
Außerdem hab ich sys.argv[0] durch __file__ getauscht, dann braucht man theoretisch kein import os ...

Ach da fällt mir gerade was für file_to_dict ein:
kann man nicht das:

Code: Alles auswählen

    f = file( FileName,'r' )
    c = f.read()
    f.close()
kürzen in das:

Code: Alles auswählen

c = file( FileName,'r' ).read()
In der Variante erübrigt sich das close(), weil es automatisch ausgeführt wird, hab ich mal irgendwo aufgeschnappt...

Verfasst: Donnerstag 9. Dezember 2004, 00:06
von Dookie
Hi jens,
jens hat geschrieben:Ach da fällt mir gerade was für file_to_dict ein:
kann man nicht das:

Code: Alles auswählen

    f = file( FileName,'r' )
    c = f.read()
    f.close()
kürzen in das:

Code: Alles auswählen

c = file( FileName,'r' ).read()
In der Variante erübrigt sich das close(), weil es automatisch ausgeführt wird, hab ich mal irgendwo aufgeschnappt...
kann man, ich find es aber sauberer wenn man Resourcen die man belegt auch wieder explizit freigibt. Also zu jedem open oder file auch ein entsprechendes close setzt. Man gewöhnt sich sonst "Unarten" an, die in anderen Programmiersprachen zu oft schlimmen und schwer zu findenden Fehlern führen können. Auch wenn ich sonst gerne Code auf eine Zeile zusammenquetsche, bin ich in dem Fall für den Dreizeiler :)


Gruß

Dookie

Verfasst: Donnerstag 9. Dezember 2004, 07:45
von jens
Aber wird denn jetzt bei der einzeiler-Variante automatisch ein .close() gemacht oder nicht???

Wenn ja, finde ich es völlig legitim!

Verfasst: Donnerstag 9. Dezember 2004, 12:50
von Leonidas
jens hat geschrieben:Aber wird denn jetzt bei der einzeiler-Variante automatisch ein .close() gemacht oder nicht???
Ja, der Python Interpreter macht das beim Beenden für alle noch offenen file-like's. Aber ich stimme da Dookie zu dass es besser ist von selbst zu schließen.

Verfasst: Donnerstag 9. Dezember 2004, 12:51
von jens
Achso, erst beim beenden... Pfui... Dann hab ihr absolut recht!

Verfasst: Donnerstag 9. Dezember 2004, 12:58
von Leonidas
jens hat geschrieben:Achso, erst beim beenden... Pfui... Dann hab ihr absolut recht!
Oder vielleicht auch schon wenn der GC drüberläuft. Aber auf jeden Fall ist es besserer Programmierstil das selbst zu machen.

Verfasst: Donnerstag 9. Dezember 2004, 14:11
von Dookie
Du kannst dir ja auch eine Funktion schreiben die den Inhalt einer Datei zurückliefert.

Code: Alles auswählen

def file_content(fname):
    f = open(fname, 'r')
    content = f.read()
    f.close()
    return content
Dann reicht im Programm ein inhalt = file_content("foo.bar").


Gruß

Dookie

Verfasst: Dienstag 4. Januar 2005, 16:34
von mitsuhiko
Ich habe das eigentlich immer mit eval gemacht:

Code: Alles auswählen

def file2dict(name):
    return eval(file(name, "r").read())
    
def dict2file(name, dct):
    file(name, "w").write(str(dct))
Aber weil man ja eval nicht verwenden soll (Warum eigentlich?) werde ich jetzt auch deine Methoder verwenden.
Allerdings hat das mit eval den Vorteil, dass keine Informationen verlohren gehen. Int bleibt int, list bleibt list.

Verfasst: Dienstag 4. Januar 2005, 16:35
von genrich
Was passiert mit eval(), wenn jemand die Datei manipuliert und ein sys.exit() darin schreibt ;)

Verfasst: Dienstag 4. Januar 2005, 17:35
von Dookie
sys.exit() is ja noch ned schlimm, ein "import os; os.system('format c:')" unter windows reingeschmuggelt in die Datei und es kommt freude auf ;)


Gruß

Dookie

Verfasst: Dienstag 4. Januar 2005, 20:33
von Milan
Hi Blackbird. Siehst ja, was man da schönes mit machen kann :wink: wenn du alternativ beliebige Datenstrukturen von Python abspeichern willst (in einem Dict) dann schau dir doch mal die Module shelve und pickle an, damit geht das genauso bequem aber viel sicherer als mit eval.

@Dookie: eval kann keine Statements ausführen... os muss schon importiert sein, damit es läuft... was es aber nicht annähernd besser macht, da dies oft genug der Fall ist.

Verfasst: Dienstag 4. Januar 2005, 21:06
von Dookie
Milan hat geschrieben:@Dookie: eval kann keine Statements ausführen... os muss schon importiert sein, damit es läuft... was es aber nicht annähernd besser macht, da dies oft genug der Fall ist.
Da ich eval nie verwende, wusst ich das nicht, aber wie du schon sagst, ist os ja oft genug schon importiert.


Gruß

Dookie