Seite 1 von 1

Pfad aus Textdatei benutzen

Verfasst: Dienstag 27. November 2012, 13:43
von Stefan990
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?

Re: Pfad aus Textdatei benutzen

Verfasst: Dienstag 27. November 2012, 13:53
von sparrow
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.

Re: Pfad aus Textdatei benutzen

Verfasst: Dienstag 27. November 2012, 13:56
von EyDu
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.

Re: Pfad aus Textdatei benutzen

Verfasst: Dienstag 27. November 2012, 17:16
von Stefan990
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.

Re: Pfad aus Textdatei benutzen

Verfasst: Dienstag 27. November 2012, 17:42
von EyDu
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.

Re: Pfad aus Textdatei benutzen

Verfasst: Dienstag 27. November 2012, 20:29
von sparrow
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'

Re: Pfad aus Textdatei benutzen

Verfasst: Dienstag 27. November 2012, 23:59
von /me
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.

Re: Pfad aus Textdatei benutzen

Verfasst: Mittwoch 28. November 2012, 09:45
von Stefan990
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?!

Re: Pfad aus Textdatei benutzen

Verfasst: Mittwoch 28. November 2012, 10:03
von EyDu
Strings haben eine strip-Methode, damit wirst du den Zeilenumbruch los.

Re: Pfad aus Textdatei benutzen

Verfasst: Mittwoch 28. November 2012, 10:19
von /me
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?).

Re: Pfad aus Textdatei benutzen

Verfasst: Mittwoch 28. November 2012, 12:16
von Stefan990
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])

Re: Pfad aus Textdatei benutzen

Verfasst: Mittwoch 28. November 2012, 14:27
von 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.