Datei kopieren

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.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

diesmal versuche ich gerade total verzweifelt eine Datei (hier eine INI-Datei) zu kopieren und an einem belieben Ort einzufügen. Eine Datei von Ort A nach Ort B kopieren. Dazu einen Ausschnitt vom Quelltext:

Code: Alles auswählen

        self.ui_pp_NoFileError.pushButtonGo.clicked.connect(self.copy_ini_file)

    def copy_ini_file(self):
        setting_folder = os.path.abspath(os.path.join('settings', os.linesep))
        try:
            shutil.copy2(self.inputfile, setting_folder)
        except shutil.Error as e:
            print('Error: %s' % e)
        except IOError as e:
            print('Error: %s' % e.strerror)

    def load_config_file(self):
        self.inputfile = (QFileDialog.getOpenFileName(self, 'Select configuration file',
                                                         '',
                                                         "configuration files (*.ini)"))
Was hatte ich vor? Nachdem eine Datei über die load_config_file-Funktion mittels eines Datei-Dialoges (QFileDialog) ausgewählt wurde, soll der gesamte Pfad, einschließlich der Dateiname und die dazugehörige Dateiendung, also der komplette Pfad in das Attribut inputfile gespeichert werden. Weshalb ich dieses Attribut an 'self' binde, liegt daran, weil ich im späteren Verlauf auf das Attribut zugreifen möchte. Beim Klicken auf die Schaltfläche pushButtonGo soll die copy_ini_file-Funktion aufgerufen werden. In dieser Funktion wird ein zusammengesetztes Pfad in die lokale Variable setting_folder gespeichert. Im TRY...EXCEPT-Block möchte ich die Fehler entsprechend behandeln. Im TRY versuche ich die Datei (inputfile) in den festgelegten Ordner (setting_folder) zu kopieren. Die Datei liegt auf meinem Desktop (Windows 7) und möchte sie gern auf eine Partition kopieren. Für den Fall, dass die Frage nach dem Administrator-Recht aufkommt.

Und bei diesem Versuche bekomme ich folgende Fehlermeldung:
Traceback (most recent call last):
File "D:\Dan\Python\project_xarphus\files\modules_ui\ui_pp_NoFileError.py", line 43, in copy_ini_file
shutil.copy2(self.inputfile, setting_folder)
File "C:\Python27\lib\shutil.py", line 130, in copy2
copyfile(src, dst)
File "C:\Python27\lib\shutil.py", line 68, in copyfile
if _samefile(src, dst):
File "C:\Python27\lib\shutil.py", line 63, in _samefile
return (os.path.normcase(os.path.abspath(src)) ==
File "C:\Python27\lib\ntpath.py", line 471, in abspath
D:\Dan\Python\project_xarphus\settings\

path = _getfullpathname(path)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xdc in position 9: ordinal not in range(128)
Traceback (most recent call last):
File "D:\Dan\Python\project_xarphus\files\modules_ui\ui_pp_NoFileError.py", line 43, in copy_ini_file
shutil.copy2(self.inputfile, setting_folder)
File "C:\Python27\lib\shutil.py", line 130, in copy2
copyfile(src, dst)
File "C:\Python27\lib\shutil.py", line 68, in copyfile
if _samefile(src, dst):
File "C:\Python27\lib\shutil.py", line 63, in _samefile
return (os.path.normcase(os.path.abspath(src)) ==
File "C:\Python27\lib\ntpath.py", line 471, in abspath
path = _getfullpathname(path)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xdc in position 9: ordinal not in range(128)
BlackJack

@Sophus: Wenn ich raten müsste verschluckt sich da eine interne Funktion für Windowspfade daran das Du statt `str` oder `unicode` da ein `QString`-Objekt übergibst. Und das enthält etwas ausserhalb von ASCII. Ich tippe auf ein 'Ü'‽ Wandel den Wert vorher in ein `unicode`-Objekt um, dann sollte das Problem behoben sein.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Nicht ganz. Hier, die Umwandlung in unicode:

Code: Alles auswählen

    def copy_ini_file(self):
        setting_folder = os.path.abspath(os.path.join('settings', os.linesep))
        print setting_folder
        try:
            shutil.copy2(unicode(self.inputfile), unicode(setting_folder))
        except shutil.Error as e:
            print('Error (shutil.Error): %s' % e)
        except IOError as e:
            print('Error (IOError): %s' % e.strerror)
Er wirft zwar keine TraceBack-Meldungen, jedoch aber behandelt der Try-Except-Block den Fehler, und das siieht wie folgt aus:
D:\Dan\Python\project_xarphus\settings\

Error (IOError): invalid mode ('wb') or filename
D:\Dan\Python\project_xarphus\settings\

Error (IOError): invalid mode ('wb') or filename
Liegt es daran, weil die Test-Datei leer ist? Ansonsten weiß ich nicht, was er mit 'wb' meint.
BlackJack

@Sophus: Na hier geht es jetzt aber um die Zieldatei. Erklär mal was Du Dir bei *dem* Dateinamen gedacht hast. (Wobei mich ein wenig wundert das der illegal ist, aber er ist auf jeden Fall ziemlich ungewöhnlich.)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Die Zieldatei ist eine INI-Datei, mit dem Namen test.ini. Diese Datei soll Illegal sein? settings ist ein Ordner.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich glaube, ich hab den Fehler gefunden, jedoch will er mir nicht begreiflich sein.

Vorher:

Code: Alles auswählen

setting_folder = os.path.abspath(os.path.join('settings', os.linesep))
Nachher

Code: Alles auswählen

setting_folder = os.path.abspath(os.path.join('settings'))
Bei os.linesep hatte ich folgende Überlegung. Ich wollte am Ende des Ordners (settings) einen Backslash, also diesen '\'. So nahm ich an, dass ich dann auch in dem Ordner settings bin, und die Datei dort hinein kopieren kann.
BlackJack

@Sophus: Ob da am Ende des Pfades ein Backslash steht oder nicht ist egal. Du fügst da aber gar keinen Backslash, also keinen *Pfad*trenner, sondern `os.linesep` an, also den *Zeilen*trenner. Wenn das funktionieren würde hättest Du am Ende im Verzeichnis ``D:\Dan\Python\project_xarphus\settings\`` eine Datei gehabt die als Dateinamen einen einzelnen Zeilenumbruch hätte. Das scheint unter Windows kein zulässiger Dateiname zu sein. :-)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Python ist es wirklich egal, ob am Ende des Pfades ein Backslash vorhanden ist oder nicht? Ich dachte immer, sowas sein wichtig, vor allem, wenn man in bestimmte Ordner wechseln will. Was wäre denn in diesem Fall ein Backslash? Ein 'os.sep'?
BlackJack

@Sophus: Das ist nicht nur Python egal das ist vor allem dem Betriebssystem egal. Das ist ein Pfad*trenner* muss zwischen den Verzeichnisnamen stehen um die trennen zu können. So auf Anhieb fällt mir nur ein Programm ein das einen Unterschied zwischen einem Pfad mit Trenner am Ende und ohne macht und das wäre ``rsync``. Normalerweise ist das völlig egal.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Auch unter Linux oder betrifft das jetzt nur Windows? Ich bin leider unter Linux nicht so sehr bewandert.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: da Du nicht weißt, was Du tuts, machst Du aus gutem Willen noch etwas viel schlimmeres. Beginnt ein Teil-Pfad mit einem Pfadtrenner, so wird der Teil-Pfad als absoluter Pfad angesehen. Das hättest Du übrigens in der interaktiven Konsole ganz leicht selbst herausfinden können:

Code: Alles auswählen

>>> os.path.abspath(os.path.join('settings', os.sep))
'C:\\'
@BlackJack: Windows scheint jedes Zeichen <= '\x1f' als illegal anzusehen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute, ich hätte gern eure Meinung und Kritikpunkte. Nachdem die Datei nun erfolgreich in den entsprechenden Ordner kopiert wurde, soll nun geschaut werden, ob die INI-Datei einen bestimmten Namen hat. Dies ist insofern wichtig, weil im Programm die Information, hier der Name der INI-Datei, hart kodiert wurde. Ist ja auch üblich, wenn man auf bestimmte Dateien zugreifen will. Ich habe dazu eine Funktion geschrieben, die auch weitestgehend funktioniert. Jedoch hätte ich eure Meinung, inwieweit man noch etwas optimieren könnte:

Code: Alles auswählen

    def rename_ini_file(self):
        import re
        list_dir = os.listdir(self.setting_folder)
        files = [f for f in list_dir if re.search('.ini', f, re.I)]
        for element in files:
            if element == "config.ini":
                print "All right, there is already a file named config.ini"
            else:
                print "There isn't a file named config.ini"
                try:
                    old_path = os.path.abspath(os.path.join('settings', element))
                    new_path = os.path.abspath(os.path.join('settings', 'config.ini'))
                    os.rename(old_path,  new_path)
                except IOError as er:
                    print('Error (IOError): %s' % er.strerror)
                except OSError as e:
                    print (' Error (OSError): %s' % e.strerror)


    def copy_ini_file(self):
        self.setting_folder = os.path.abspath(os.path.join('settings'))
        try:
            shutil.copy2(unicode(self.inputfile), unicode(self.setting_folder))
            self.rename_ini_file()
        except shutil.Error as e:
            print('Error (shutil.Error): %s' % e)
        except IOError as e:
            print('Error (IOError): %s' % e.strerror)
Was habe ich mir dabei gedacht? Nachdem die Datei durch die copy_ini_file-Funktion in den entsprechenden Ordner kopiert wurde, so wird dann die rename_ini_file-Funktion aufgerufen, ansonsten springt das Programm in die Exceptions und behandelt die Fehler bzw. Ausnahmefehler. Ich weiß, dass man Importe immer am Anfang des Moduls macht. Dies dient erst einmal zu Testzwecken. Später werden die Importe verlegt.

Um zu sehen, ob ich wirklich verstehe was vor sich geht:
Zeile 2: Für reguläre Ausdrücke wurde das Modul re importiert
Zeile 3: "Alle" Dateien werden im Pfad self.setting_folder gesucht.
Zeile 4: Hier werden jene Dateien ermittelt, die auf “.ini” enden. Alle anderen Dateien sind uninteressant.
Zeile 5: In der For-Schleife werden die Dateien iteriert-
Zeile 6: In der If-Kaskade wird nun geschaut, ob es bereits eine Datei mit dem Namen 'config.ini' existiert
Zeile 10: Wenn nicht, dann wird versucht eine Datei umzubenennen.
Zeile 11 und 12: Damit die Dateien umbenannt werden können, brauche ich den aktuellen Pfad (old_path) und den neuen Pfad (new_path).
Zeile 13: Datei wird umbenannt

Ach ja, bei einer Zeile bleibe ich rein verständnishalber stecken:

Code: Alles auswählen

        files = [f for f in list_dir if re.search('.ini', f, re.I)]
Wie weit kommt mein Verständnis? Ich weiß, dass es sich hierbei um eine Lsit Comprehension handelt. Und durch die regulären Ausdrücke wird in der Liste (list_dir) nach den Elementen der Liste mit der Endung ini gesucht. Hierbei handelt es sich um Dateien mit der Endung auf ini. Die Variable f ist hier das iterierte Element aus der Liste list_dir. Aber was macht re.I genau?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

re.I, das hättest du aber auch leicht selbst nachschlagen können.

glob ist hier besser geeignet als reguläre Ausdrücke. Ein einfaches ``filename.endswith("ini")`` würde es auch tun. Oder, noch viel einfacher: Du suchst nach einer Datei, also versuche sie einfach umzubenennen. Wenn sie nicht existiert, dann bekommst du schon eine Ausnahme.

Es wurde dir glaube ich schon öfter mal gesagt: Importe sollten ganz oben im Modul stehen, nicht in Funktionen. An deinen Namen solltest du auch wieder mal arbeiten. In "files" stecken Datei*namen* und keine Datei. Methoden welche self nicht nutzen sollten auch keine Methoden sein, sondern einfache Funktionen. Und das "self.setting_folder" sieht sehr verdächtig aus. Soll das wirklich ein Attribut sein? Mal ganz davon abgesehen, dass es konstant ist.

Ist Die Meldung in Zeile 9 falsch oder soll sie in die Irre leiten? Es können ja durchaus mehrere ini-Dateien existieren.
Das Leben ist wie ein Tennisball.
BlackJack

@Sophus: Das funktioniert aber auch nur für eine komische Definition von ”funktioniert” es sei denn man will das Verhalten was diese Funktion beschreibt tatsächlich haben. Was ich mir irgendwie nicht vorstellen kann.

Was `re.I` bedeutet kann man erfahren wenn man nachliest was das dritte Argument von `re.search()` bedeutet und welche Werte man dort übergeben kann. Wie kommst Du darauf das Argument anzugeben wenn Du nicht weisst was es bedeutet? Andererseits sorgt die `search()`-Funktion schon für den ersten Fehler denn das Muster trifft auf jede Zeichenkette zu die eine Teilzeichenkette beinhaltet welche aus *irgendeneinem* Zeichen gefolgt von der Zeichenfolge 'ini' besteht. Also 'Panini_Bilder.txt' wäre ein Treffer:

Code: Alles auswählen

In [94]: re.search('.ini', 'Panini_Bilder.txt', re.I)
Out[94]: <_sre.SRE_Match at 0xa22ffa8>

In [95]: bool(re.search('.ini', 'Panini_Bilder.txt', re.I))
Out[95]: True
Ob eine Zeichenkette mit einer Teilzeichenkette endet kann man aber auch ganz ohne `re`-Modul prüfen. Da haben Zeichenketten eine Methode für.

Mal angenommen `files` würde tatsächlich nur Dateinamen enthalten die auf '.ini' enden, dann ist das immer noch reichlich merkwürdig. Alle Dateien, ausser einer die tatsächlich schon 'config.ini' heisst würden der Reihe nach in 'config.ini' umbenannt. Was eine sehr merkwürdige Aktion ist. Warum würde man so etwas wollen?

Das ist IMHO auch keine wirkliche Methode sondern eher eine Funktion. Und die Fehlerbehandlung erscheint mir auch nicht sinnvoll. Der Aufrufer bekommt ja gar nicht mit dass da etwas nicht geklappt hat und das Programm geht im weiteren dann davon aus das es eine 'config.ini' gibt, nur um dann wahrscheinlich beim Versuch darauf zuzugreifen *wieder* in eine Ausnahme zu laufen. Wenn man eine Ausnahme nicht sinnvoll behandeln kann, sollte man es gar nicht tun.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@EyDu: Kann es sein, dass du meinen Beitrag nur bis zur Hälfte liest oder aber nur den Quelltext liest und dann abbrichst? Denn, dass die Importe immer am Anfang gesetzt werden, weiß ich, und habe ich auch im Text bereits erwähnt :-) Für den Testzweck habe ich den Import erst einmal in eine Funktion untergebracht. Und zu den Namen. Du hast Recht, ich muss mich da noch etwas konsequenter ransetzen. Und zu deiner Annahme, dass 'glob' besser geeignet sei als die regulären Ausdrücken. Wieso, wenn ich fragen darf? Hat meine Variante eher den Effekt wie mit einer Kanone auf einen Spatzen zu schießen? Und Zeile 9 ist doch nicht irreführend? Ich meine, es wird ja geschaut, ob es die Datei (Element) 'config.ini' in der Liste (list_dir) gibt, wenn nicht, dann gibt es die Datei nicht. Andere INI-Dateien sollten mich erst einmal nicht interessieren.

Aber ich glaube, ich habe eben einen Fehler in meiner Variante entdeckt. Ich kopierte eine INI-Datei in mein Verzeichnis, und benannte sie 'config1.in'. Logischerweise 'denkt' sich mein Programm, dass es die Datei config.ini nicht gibt, auf die er zugreifen will. Er gibt mir also ein entsprechendes Fenster aus, ich suche dann spielerisch eine andere INI-Datei von meinem Desktop (test.ini), die dann zum entsprechenden Ordner kopiert, und anschließend umbenannt werden soll. Eigentlich müssten dann zwei INI-Dateien im Ordner vorzufinden sein, einmal 'config1.ini' und dann die kopierte, und umbenannte Datei 'config.ini'. Und was sehe ich? Ich sehe dann 'config.ini' und 'test.ini'. Die andere INI-Datei (config1.ini) ist nicht mehr da. Ich denke, dass mein Programm an dieser Stelle, die erst beste INI-Datei schnappt, sobald mehrere im Ordner sind, und benennt sie dann um. Die kopierte Datei lässt er in Ruhe, daher behält sie auch ihren ursprünglichen Namen, eben 'test.ini'.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Ich sehe schon, ich habe mächtig Fehler eingehandelt. Mein Anliegen war es ja, dass die Datei, die kopiert, und in den entsprechenden Ordner eingefügt wird, auch den richtigen Namen bekommt. Es kann ja sein, dass man von der INI-Datei eine Sicherungskopie macht, und sie dann von mir aus "config_original.ini" benennt, und diese dann auf dem Desktop speichert. Wenn der Anwender diese Datei durch QFileDialog diese Datei aus dem Desktop aussucht, so kopiert Python diese Datei zum vorgesehenen Ordner. Und im Anschluß soll die Datei so umbenannt werden, das Python auch damit arbeiten kann, da der Name der Datei hart kodiert im Quelltext hinterlegt ist. Ich hatte ja auch die Idee, irgendwie an den Dateinamen direkt ranzukommen, nachdem der Anwender die Datei ausgesucht hat, diesen Namen dann in eine Variable speichern und nach dem Kopiervorgang dann abgleichen und gegebenenfalls umzubenennen. Aber irgendwie will es mir nicht sonderlich klappen. Daher habe ich diese Funktion konstruiert.
BlackJack

@Sophus: Das erscheint mir alles sehr umständlich. Warum gibst Du nicht gleich beim kopieren schon den neuen/festen Namen als Zielnamen an? Dann kann man sich das extra umbenennen sparen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Weil ich Angst hatte, dass die originale Datei umbenannt wird, und nicht die, die kopiert werden soll. Wenn ich dich also richtig verstehe, so soll die os.rename()-Methode gleich direkt in der copy_ini_file-Funktion angelegt werden? Ich werde mal schauen, wie ich das machen kann.
BlackJack

@Sophus: Du brauchst gar kein `os.rename()`. Nochmal: Als Ziel den Zieldateinamen gleich so angeben wie er am Ende aussehen soll.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Jetzt sag nicht, etwa so?

Code: Alles auswählen

    def copy_ini_file(self):
        self.setting_folder = os.path.abspath(os.path.join('settings', 'config.ini'))
        print "setting_folder: ", self.setting_folder
        try:
            shutil.copy2(unicode(self.inputfile), unicode(self.setting_folder))
            print "File was copied"
        except shutil.Error as e:
            print('Error (shutil.Error): %s' % e)
        except IOError as e:
            print('Error (IOError): %s' % e.strerror)
In Zeile 2 habe ich den Dateinamen festgelegt. Wenn es das ist was du meintest, dann renn ich schreiend vor die Tür. Weil ich vollkommen anders dachte, und zwar, dass Python auf diesem Weg eine neue Datei anlegt, wenn sie nicht vorhanden ist, und nicht die ursprüngliche Datei kopiert und in einem Rutsch umbenennt. Deswegen dachte ich, ich werde erst einmal kopieren, und dann umbenennen, damit ich auch sicher gehen kann, dass auch die Datei umbenannt und nicht neu erstellt wurde.
Antworten