shutil, deutsche Umlaute und utf-8

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
7tupel
User
Beiträge: 3
Registriert: Samstag 13. November 2010, 23:45

hallo zusammen,

ich arbeite gerade an einem kleinen Programm das diverse Metainformationen zu Dateien aus einem XML file liest und die einzelnen Files abhängig von den dazugehörigen Metainformationen in andere Ordner verschiebt.
Mein Problem hierbei ist, dass in den urls teilweise Umlaute vorkommen. Wenn ich die XML Datei per fopen öffne (im utf-8 mode), die Pfadangaben extrahiere und meine Originalfiles per shutil.copyfile() kopieren möchte, dann bekomme ich die Fehlermeldung der Art

Code: Alles auswählen

IOError: [Errno 2] No such file or directory: u'/Volumes/Disk1/Files/Folder/O%CC%88kologie/doku.pdf'
Der Pfad ist in einem utf-8 kodiertem string gespeichert, nachdem er aus dem XML file extrahiert wurde.

Daher meine Frage, wie bekomme ich es hin die files ohne Fehler zu kopieren. Muss ich den utf-8 string erst umkodieren? Wenn ja in welches Format und wie?

lg 7tupel
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Was ist "fopen()"? Mit welchem Parser parst Du denn die XML-Datei? Eigentlich sonderbar, dass der "utf-8"-codierte Strings zurückgibt; man würde eher Unicode erwarten. Auf jeden Fall solltest Du die Angaben in Unicode wandeln. Zeig doch mal den Code vom Parsing.

Ansonsten zur Frage: In der Doku zu shutil.copyfile() steht ja, dass die Pfadangaben als Strings erfolgen sollen. Also solltest Du sie im Filesystem-Encoding codieren denke ich mal. Das findest Du so raus:

Code: Alles auswählen

In [163]: sys.getfilesystemencoding()
Out[163]: 'mbcs'
Nun musst Du also nur noch die Pfade von Unicode in dieses Encoding codieren:

Code: Alles auswählen

path.encode(sys.getfilesystemencoding())
Zu dem Thema beachte meine Sig :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

Wobei das gezeigte Beispiel jetzt gar kein UTF-8 kodierter Bytestring ist, sondern tatsächlich Unicode aber mit URL-kodierten Bytewerten. Was bei einer URL nicht besonders überraschen sollte. Dem sollte man mit `urllib.unquote()` vielleicht entgegnen können.
7tupel
User
Beiträge: 3
Registriert: Samstag 13. November 2010, 23:45

ups, meinte natürlich "open()", fopen() ist C++...

Ich habe mir meinen eigenen kleinen PDA geschrieben um das XML File zu parsen, da der DOM Parser von Python zu lange braucht (brauche auch keinen kompletten DOM Baum) und SAX hilft mir nur bedingt weiter da ich erst die ganze Datei lesen muss bevor ich die Daten verarbeiten kann.

Das Encoding hab ich jetzt gelöst. Problem war, dass die Pfade im XML file nicht Unicode/utf-8 codiert sind, sondern in einer mir leider unbekannten Kodierung. Ich hab es jetzt gelöst indem ich den string der jeweiligen Zeile der Datei durchsuche und die Kodierung quasi per Hand in Unicode umwandle.

lg 7tupel
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Klingt alles ein wenig krude... was ist ein PDA? Hast Du Dir mal lxml angeguckt? Das ist eigentlich pfeilschnell und bietet auch Streaming-Methoden; iirc gabs da einen netten Artikel von einem IBM-Mitarbeiter dazu.

Und zum Encoding hat Dir BlackJack ja auch schon einen Hinweis gegeben - den hast Du jetzt vermutlich selber nachgebaut - da sollte man imho die eingebauten Funktionen liefern, da diese sicherlich besser getestet sind als eigene Kreationen.

Zudem würde ich intern immer mit Unicode arbeiten und nicht mit Bytestrings.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
7tupel
User
Beiträge: 3
Registriert: Samstag 13. November 2010, 23:45

Also ein PDA (Push Down Automaton, Kellerautomat) ist eine Maschine die Chomsky-2 Grammatiken (Kontextfreie Grammatiken) verarbeiten kann. PDAs werden in Compilern (und XML Parsern) eingesetzt, um aus den einzelnen Token einen Syntaxbaum aufzubauen und um den Code auf Syntaktische Korrektheit zu überprüfen.
Genau so ein PDA wird auch im DOM Parser (und höchstwahrscheinlich auch im lxml Parser) verwendet. In meinem Fall ist es aber nicht nötig, dass ich einen kompletten Syntaxbaum aufbaue, da ich ca. 50% davon überhaupt nicht brauche. Es ist hier also effizienter nur das zu parsen was ich später auch nutze. So spare ich Speicher und Rechenzeit. (Da mein XML File ca. 30MB groß ist fällt das ziemlich ins Gewicht).
Bei Gelegenheit werde ich aber trotzdem mal lxml ausprobieren.

Klar wäre es sicher effektiver eine fertige Funktion zu nutzen, um die Zeichen zu encoden. Aber ich weiß leider weder welche Kodierung verwendet wird (%20 = " ", O%CC%88 = "Ö", ...) noch welche Funktion die richtige ist, um das in Unicode zu wandeln. Wenn ihr mir hier einen Tipp geben könnt gerne.

Der Rest ist inzwischen auf Unicode umgestellt. Aber an manchen Stellen muss ich einfach auf utf-8 Encoding zurückgreifen, da das das Standard Encoding von meinem System ist.

lg 7tupel
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

7tupel hat geschrieben:Also ein PDA (Push Down Automaton, Kellerautomat) ist eine Maschine die Chomsky-2 Grammatiken (Kontextfreie Grammatiken) verarbeiten kann. ...
Danke, so ausführlich wärs gar nicht nötig gewesen - Kellerautomat hätte mir sofort etwas gesagt; auf das englische Synonym bin ich eben nicht gekommen :-D
7tupel hat geschrieben: Klar wäre es sicher effektiver eine fertige Funktion zu nutzen, um die Zeichen zu encoden. Aber ich weiß leider weder welche Kodierung verwendet wird (%20 = " ", O%CC%88 = "Ö", ...) noch welche Funktion die richtige ist, um das in Unicode zu wandeln. Wenn ihr mir hier einen Tipp geben könnt gerne.
Hat Dir BlackJack doch geschrieben. Guck noch mal in sein Posting.
7tupel hat geschrieben: Der Rest ist inzwischen auf Unicode umgestellt. Aber an manchen Stellen muss ich einfach auf utf-8 Encoding zurückgreifen, da das das Standard Encoding von meinem System ist.
Naja, bei I/O-Operationen muss man natürlich wandeln, intern solltest Du da aber nix mischen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten