Pfad aus Textdatei benutzen

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
Stefan990
User
Beiträge: 4
Registriert: Dienstag 27. November 2012, 13:20

Hallo,

vorab, das Modul os.path ist mir bekannt.
Ich habe einen Pfad mit einem Programm in einer txt-Datei abgespeichert. Das sieht dann z.B. so aus:

Code: Alles auswählen

C:\a-folder\t-folder
Nun möchte ich diesen Pfad in meinem Programm verwenden, um Dateien zu kopieren:

Code: Alles auswählen

import os
import shutil
import codecs

timepath = os.path.dirname(os.path.abspath(__file__))
logfile = os.path.join(timepath, "backup_log.txt")
fobj = codecs.open(logfile, "r", "utf-8")
backupfile = []
originpath = []
for line in fobj:
	if "Original:" in line:
		originpath.append(os.path.dirname(line.replace("Original:", "")))
	elif "Backup:" in line:
		backupfile.append(line.replace("Backup:", ""))
fobj.close()

x = 0 
while x <= len(backupfile)-1:
	shutil.copy(backupfile[x],originpath[x])
	x = x+1
Leider funktioniert das nicht.
Selbst wenn ich den Pfad vorher mittels os.path.normcase konvertiere werden manche Namen falsch interpretiert:

Code: Alles auswählen

os.path.normcase("C:\a-folder\t-folder")
'c:\x07-folder\t-folder'
Gibt es keine Funktion, die den Namen einfach übernimmt ohne Dinge wie "\a" zu interpretieren?
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Wenn du den Pfad selbst in den Quelltext eingibst, dann musst du einen Backslash maskieren:

Code: Alles auswählen

>>> falsch = "c:\a-temp"
>>> falsch
'c:\x07-temp'
>>> richtig = "c:\\a-temp"
>>> richtig
'c:\\a-temp'
Aber: der doppelte Babschlash ist nur die Darstellung von diesem, intern ist das nur ein Backslash.

Code: Alles auswählen

>>> a = "\\"
>>> len(a)
1
Überleg dir, dass ein Zeilenumbruch mit \n dargestellt wird. Jedes Verzeichnis, das mit n anfängt könntest du also nicht verwenden.

Aber: das kann nicht für den String gelten, den du aus einer Datei liest.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo und willkommen im Forum!

Da gibt es gleich mehrere Lösungen: raw-Strings, doppelte Backslashes oder, die sauberste Variante, mittels os.path.join.

Noch zu deinem Code:

- Dateien solltest du mittels with-Statement öffnen, dann kannst du das Schließen auch nicht mehr vergessen.
- Über Listen kannst du direkt mit einer for-Schleife Iterieren. Brauchst du dazu noch einen Index, dann hilft dir die enumerate-Funktion. Willst du über zwei Listen parallel iterieren, dann hilft dir zip weiter. Die verwendung einer while-Schleife ist hier vollkommen falsch.
- ``while x <= y-1`` lässt sich leichter ausdrücken als ``while x < y``
- Benutze Konstanten
- Verwende vernünftige Namen. In backupfile steckt doch überhaupt keine Datei.
- x ist ein ungewöhnlicher Name für eine Zählvariable. Für ganze Zahlen verwendet man in der Regel i, j oder k. Am besten natürlich einen aussagekräftigen Namen.
Das Leben ist wie ein Tennisball.
Stefan990
User
Beiträge: 4
Registriert: Dienstag 27. November 2012, 13:20

Hallo,

erstmal vielen Dank für die schnellen Antworten! Leider schaffe ich es nicht, den Pfad direkt in der "\\" Form in die Textdatei zu speichern. Könnt ihr mir evtl ein kurzes Beispiel geben, wie ich den Pfad korrekt in die Textdatei schreiben und anschließen wieder korrekt auslesen kann? Es reicht für eine Zeile (=einen Pfad), den Rest krieg ich dann schon hin.
Bisher sieht das (gekürzt) so aus:

Code: Alles auswählen

path = os.path.join(irgeneinpfad, 'beispiel.txt')
fobj = open(textfilepath, "w") 
fobj.write(path)
fobj.close()
So wird eine Textdatei mit folgendem Inhalt erstellt:

Code: Alles auswählen

C:\Ordner\beispiel.txt


Beim auslesen habe ich dann schon keine Chance mehr den Pfad zu "korrigieren", da die backslashes falsch interpretiert werden.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

sparrow hat es doch schon geschrieben: du musst die Darstellung eines Zeichen und das Zeichen unterscheiden. Der einfache Backslash in einer Textdatei entspricht dem doppeltem Backslash im Python-Code. Wenn du die Zeile wieder aus einer Datei liest, dann wird natürlich auch das Zeichen wieder korrekt gelesen. Du hast also gar kein Problem.

Außerdem solltest du dir angewöhnen Dateien mittels with-Statement zu öffnen. Dann sparst du dir das explizite Schließen der Datei und bist auch vor Exceptions in Sicherheit.
Das Leben ist wie ein Tennisball.
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Zeig uns doch mal in einem einfachen, kurzen Beispiel wie du aus der Datei liest, und es dabei schief geht. Wie oben beschrieben, kann es eigentlich nicht falsch sein:

Code: Alles auswählen

sparrow@terra:/tmp$ cat test.txt
\ein\Pfad\a\mit\vielen\Backslashes

sparrow@terra:/tmp$ python
>>> with open("test.txt") as f:
...  firstline=f.readline()
...
>>> firstline
'\\ein\\Pfad\\a\\mit\\vielen\\Backslashes\n'
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Stefan990 hat geschrieben:So wird eine Textdatei mit folgendem Inhalt erstellt:

Code: Alles auswählen

C:\Ordner\beispiel.txt


Beim auslesen habe ich dann schon keine Chance mehr den Pfad zu "korrigieren", da die backslashes falsch interpretiert werden.
Python wird einen aus einer Datei gelesenen Backslash auch genau als einen Backslash interpretieren. Irgendwas machst du beim Lesen der Datei falsch.
Stefan990
User
Beiträge: 4
Registriert: Dienstag 27. November 2012, 13:20

Hallo nochmal, ich habe den Fehler mittlerweile genauer lokalisieren können. Der Pfad wird tatsächlich richtig interpretiert. Allerdings wird an den Pfad immer ein "\n" gehängt, was dann dazu führt, dass ich beim kopieren den Fehler "illegales Zeichen" bekomme:

Code: Alles auswählen

	#Ausgabe in Textdatei sieht so aus:
>>> fobj.write("Original:" + rstfile + "\n" + "Backup:" + backupfile + "\n") 
>>> backupfile
'Backup:C:\Meine Tutorials\tutorial_1\Backup\20121128_08-50\file.rst'

	#Einlesen:
>>> for line in fobj:
   if "Original:" in line:
      originpath.append(os.path.dirname(line.replace("Original:", "")))
   elif "Backup:" in line:
      backupfile.append(line.replace("Backup:", ""))
>>> shutil.copy(backupfile[0],originpath[0])
ArgumentException: Illegal characters in path.
	#Kopieren funktioniert also nicht. Sehen wir uns den ersten Pfad an:
>>> backupfile[0]
'C:\\Meine Tutorials\\tutorial_1\\Backup\\20121128_08-50\\file.rst\n'
	#Hier sieht man, dass komischerweise "\n" am Ende des Pfades dabei steht. Das war in der Textdatei noch nicht der Fall.
>>> backupfile[0].replace("\n", "")
'C:\\Meine Tutorials\\tutorial_1\\Backup\\20121128_08-50\\file.rst'
	#Hier habe ich das "\n" mit der replace Methode "weggepfuscht"
>>> backupfile[0]
'C:\\Meine Tutorials\\tutorial_1\\Backup\\20121128_08-50\\file.rst\n'
	#Und trotzdem ist es dann wieder da?!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Strings haben eine strip-Methode, damit wirst du den Zeilenumbruch los.
Das Leben ist wie ein Tennisball.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Stefan990 hat geschrieben:

Code: Alles auswählen

>>> backupfile[0]
'C:\\Meine Tutorials\\tutorial_1\\Backup\\20121128_08-50\\file.rst\n'
	#Hier sieht man, dass komischerweise "\n" am Ende des Pfades dabei steht. Das war in der Textdatei noch nicht der Fall.
Oh doch, das war ganz sicher der Fall. Die Anzeige \n ist nichts anderes als die Repräsentation eines Zeilenumbruchs.
Stefan990 hat geschrieben:

Code: Alles auswählen

>>> backupfile[0].replace("\n", "")
'C:\\Meine Tutorials\\tutorial_1\\Backup\\20121128_08-50\\file.rst'
	#Hier habe ich das "\n" mit der replace Methode "weggepfuscht"
Nein, hast du nicht. Strings sind unveränderlich. Du hast einen neuen String zurückgegeben in dem der Zeilenumbruch entfernt wurde. Du hast aber nicht den Inhalt von backupfile[0] verändert.


Es wäre bei deinem Aufbau wohl recht praktisch, wenn die zu verarbeitende Zeile den Zeilenumbruch so schnell wie möglich entfernt bekäme. Sehr flexibel ist da die strip-Methode auf Strings. Damit entfernst du links und rechts alle Whitespaces (Leerzeichen, Zeilenumbrüche, ...).

Der Code könnte dann wie folgt verändert werden.
Vorher:

Code: Alles auswählen

with open("words.txt", 'r') as fobj:
    for line in fobj:
        print(repr(line))
Nachher:

Code: Alles auswählen

with open("words.txt", 'r') as fobj:
    for line in fobj:
        line = line.strip()
        print(repr(line))
Nachher (mit Generator):

Code: Alles auswählen

with open("words.txt", 'r') as fobj:
    for line in (data.strip() for data in fobj):
        print(repr(line))
Solltest du with nicht kennen, dann schau es dir an. Das wirkt erst einmal wie sehr trockener Stoff, aber es lohnt sich. Gerade im Kontext von Dateien ist das sehr interessant und dafür brauchst du die Grundlagen auch gar nicht detailliert zu kennen. Die offensichtlichste Auswirkung ist hier erst einmal, dass die Datei am Ende des with-Blocks automatisch wieder geschlossen wird. Weitere Details bei Bedarf (in einem neuen Thread?).
Stefan990
User
Beiträge: 4
Registriert: Dienstag 27. November 2012, 13:20

Hallo,

vielen Dank für Eure Hilfe, ich habe es jetzt geschafft. Die strip()-Methode war der entscheidende Hinweis. Ich habe den Code auch etwas an Eure Tips angepasst. Hier der fertige Code:

Code: Alles auswählen

import os
import shutil

timepath = os.path.dirname(os.path.abspath(__file__))
logfile = os.path.join(timepath, "backup_log.txt")
backupfile = []
originpath = []
with open(logfile, "r") as fobj:
	for line in fobj:
		if "Original:" in line:
			path = line.replace("Original:", "")
			path = path.strip()
			originpath.append(os.path.dirname(path))
		elif "Backup:" in line:
			path = line.replace("Backup:","")
			path = path.strip()
			backupfile.append(path)
	
paar = zip(backupfile, originpath)
for i, wert in enumerate(paar): 
	shutil.copy(paar[i][0],paar[i][1])
BlackJack

@Stefan990: Was da mit `enumerate()` veranstaltet wird ist ziemlicher Unsinn. Du brauchst den Index doch überhaupt nicht. Und `wert` verwendest Du in der Schleife nicht. `wert` ist das jeweilige Paar, sollte also `paar` heissen, wären das was jetzt an `paar` gebunden ist *alle* Paare sind, also auch so heissen sollte. Wenn man es denn überhaupt an einen Namen binden möchte. Das gleiche Namensproblem hast Du bei `backupfile` und `originpath` — beides sind Sequenzen von *mehreren* Dateinamen und Pfaden. Bei der Gelegenheit eine Datei (`file`) ist etwas anderes als ein Dateiname (`str`) darum sollte man auch dort sorgfältiger mit der Benennung der Objekte sein.

Die Tests und das entfernen von 'Original:' und 'Backup:' sind nicht robust. Du möchtest weder wissen ob die Teilzeichenketten *irgendwo* vorkommen, noch einfach alle Vorkommen ersetzen. Es fehlt zu einem robusten Programm auch noch die Behandlung des Falls das eine Zeile mit keiner der beiden Zeichenketten anfängt.

Für ein robustes Programm würde ich auch nicht die beiden Listen quasi unabhängig füllen, sondern es so formulieren, dass die beiden Werte auch tatsächlich direkt aufeinanderfolgend aus der Datei gelesen werden und als Paar in *einer* Liste gespeichert werden.
Antworten