Verzeichnisstruktur erstellen

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
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

Hallo und frohe Ostern :)

Wollte fragen ob jemand weiß, wie ich die komplette Verzeichnisstruktur samt leere Dateien erstellen kann.
Ziel ist C:\ als identische leere Verzeichnisstruktur zu kopieren.

Bevor ich Anfang einen brauchbaren Algorithmus zu schreiben, frage ich sicherheitshalber nach :)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Naja Du könntest mit ``os.walk`` einmal durch die gesamte Verzeichnisstruktur gehen. Mittels ``os.path.join`` kannst Du dann einen Zielpfad mit den ausgelesenen Daten kombinieren und Verzeichnisse mittels ``os.mkdir`` anlegen. Wie man leere Dateien am besten anlegt... hm... ich würde es wohl so probieren:

Code: Alles auswählen

with open("foo", "w") as f: pass
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

Hab die Grundlage gefunden:

Code: Alles auswählen

import os

for root, dirs, files in os.walk("C:/"):
    for i in files:
        print root + "\\" + i
Das fertige Werk poste ich dann hier.
BlackJack

@Dami123: Statt ``+`` und ``'\\'`` sollte man `os.path.join()` verwenden um Pfade zusammen zu setzen.
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

Ja das stimmt! War auch nur ein schnell Test.
Wie dem auch sei hab ich nun eine weitaus bessere Lösung gefunden, da ich die Fehlermeldung "Zugriff verweigert" nicht umgehen kann.
Ich hab den Befehl shutil.copytree(src, dst) ausprobiert und modifiziert. Anstatt die Dateien zu kopieren, erstellt er nun eine leere Datei mit dem gleichen Namen im Zielverzeichnis(dst).
Zudem wurde die Funktion eingebaut, dass Dateien mit bestimmten Dateiendungen trotzdem kopiert werden können.

Praktisch kann man dies als kleines Addon ins shutil Modul einbauen, habs jetzt aber noch als extra Datei im benötigten Ordner um es in Zukunft nicht zu vergessen.

Code: Alles auswählen

"""Modified version of shutil.copytree for copytree with empty files"""

#Markierung 1 -> emptycopy muss erstellt werden
#Markierung 2 -> statt copy2(srcname, dstname) -> emptycopy(srcname, dstname)

import os
import stat
import errno

class Error(EnvironmentError):
    pass

class SpecialFileError(EnvironmentError):
    """Raised when trying to do a kind of operation (e.g. copying) which is
    not supported on a special file (e.g. a named pipe)"""

class ExecError(EnvironmentError):
    """Raised when a command could not be executed"""

try:
    WindowsError
except NameError:
    WindowsError = None

def copyfileobj(fsrc, fdst, length=16*1024):
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
        buf = fsrc.read(length)
        if not buf:
            break
        fdst.write(buf)

def _samefile(src, dst):
    # Macintosh, Unix.
    if hasattr(os.path, 'samefile'):
        try:
            return os.path.samefile(src, dst)
        except OSError:
            return False

    # All other platforms: check for same pathname.
    return (os.path.normcase(os.path.abspath(src)) ==
            os.path.normcase(os.path.abspath(dst)))

def copyfile(src, dst):
    """Copy data from src to dst"""
    if _samefile(src, dst):
        raise Error("`%s` and `%s` are the same file" % (src, dst))

    for fn in [src, dst]:
        try:
            st = os.stat(fn)
        except OSError:
            # File most likely does not exist
            pass
        else:
            # XXX What about other special files? (sockets, devices...)
            if stat.S_ISFIFO(st.st_mode):
                raise SpecialFileError("`%s` is a named pipe" % fn)

    with open(src, 'rb') as fsrc:
        with open(dst, 'wb') as fdst:
            copyfileobj(fsrc, fdst)

def copystat(src, dst):
    """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
    st = os.stat(src)
    mode = stat.S_IMODE(st.st_mode)
    if hasattr(os, 'utime'):
        os.utime(dst, (st.st_atime, st.st_mtime))
    if hasattr(os, 'chmod'):
        os.chmod(dst, mode)
    if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
        try:
            os.chflags(dst, st.st_flags)
        except OSError, why:
            if (not hasattr(errno, 'EOPNOTSUPP') or
                why.errno != errno.EOPNOTSUPP):
                raise

def copy2(src, dst):
    """Copy data and all stat info ("cp -p src dst").

    The destination may be a directory.

    """
    if os.path.isdir(dst):
        dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst)
    copystat(src, dst)

def emptycopy(src, dst):
    """Create empty file with src name.

    Copy file, when file extension in files=[]."""
    #1. Markierung
    
    files = ["txt", "py", "pdf", "doc", "odt"]
    open(dst, "w").close()
    for i in files:
            if src[-3:] == i:
                copy2(src, dst)
            elif src[-2:] == i:
                copy2(src, dst)
    

def copytree(src, dst, symlinks=False, ignore=None):
    """Recursively copy a directory tree with empty files using emptycopy().
    """
    names = os.listdir(src)

    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()
        
    os.makedirs(dst)
    errors = []
    for name in names:
        if name in ignored_names:
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree(srcname, dstname, symlinks, ignore)
            
            else:
                # Will raise a SpecialFileError for unsupported file types
                #2. Markierung
                emptycopy(srcname, dstname)
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except Error, err:
            errors.extend(err.args[0])
        except EnvironmentError, why:
            errors.append((srcname, dstname, str(why)))

    try:
        copystat(src, dst)
    except OSError, why:
        if WindowsError is not None and isinstance(why, WindowsError):
            # Copying file access times may fail on Windows
            pass
        else:
            errors.extend((src, dst, str(why)))
    if errors:
        raise Error, errors
[/size]

Benutze Win 7 -> emptycopy erstellt versteckte Dateien.
Benutzeravatar
darktrym
User
Beiträge: 785
Registriert: Freitag 24. April 2009, 09:26

Schau dir mal endswith an.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

besser: os.path.splitext
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

str.endswith() sieht gut aus und würde mir in einigen Programmen weiter helfen, schau ich mir mal an danke :)

splittext hab ich mir überlegt, halte aber [-3:] für schneller. Wenn man viele extensions rauskopieren möchte, ist esd aber die bessere Lösung, ja.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Dami123 hat geschrieben:splittext hab ich mir überlegt, halte aber [-3:] für schneller. Wenn man viele extensions rauskopieren möchte, ist esd aber die bessere Lösung, ja.
Eine Lösung für schneller zu *halten* ist selten ein guter Maßstab. Wenn, dann sollte man dies auch testen. Davon abgesehen ist das Trennen von Dateiendungen sicher kein Flaschenhals in der Anwendung. Und wenn doch, dann solltest du vielleicht nicht Python verwenden.
Das Leben ist wie ein Tennisball.
BlackJack

Mal ganz abgesehen von der Geschwindigkeit machen die beiden Varianten auch etwas unterschiedliches. ``[-3:]`` ist es egal ob da ein Punkt vor den drei Zeichen steht oder irgend etwas anderes.
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

Deswegen finde ich dir Lösung mit "endswith" praktischer, da direkt die Endung ausgelesen wird und anschließend vergleicht werden kann.

@EyDu
Die Aufgabe besteht eine Lösung für Python zu finden, sonst würde ich die Frage nicht hier stellen. Aber du hast Recht, dass es nicht die schnellste Lösung.
BlackJack

@Dami123: Du begehst den selben Fehler schon wieder: Du hältst das nicht für die schnellste Lösung. Dabei kann es gut sein, dass keine schnellere möglich ist, weil das ja nicht nur von der Sprache sondern zum Beispiel auch vom Datendurchsatz der Festplatte abhängen kann. Wenn das der begrenzende Faktor ist, dann würde auch eine „schnellere Sprache” keine schnellere Lösung liefern.

Was an dem [-3:] IMHO unschön ist, ist die implizite Abhängigkeit von der/den Längen der Dateiendungen, die man weder bei `endswith()` noch `splitext()` hat. Und ob nun `splitext()` und ein `set()` mit Dateiendungen oder `endswith()` mit einem Tupel schneller ist, müsste man Messen wenn es einen interessiert.

Das der Punkt momentan nicht berücksichtigt wird ist IMHO ein Fehler im Programm, denn so werden auch Dateien kopiert bei denen das nicht die komplette Dateiendung ist.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:Was an dem [-3:] IMHO unschön ist, ist die implizite Abhängigkeit von der/den Längen der Dateiendungen, die man weder bei `endswith()` noch `splitext()` hat. Und ob nun `splitext()` und ein `set()` mit Dateiendungen oder `endswith()` mit einem Tupel schneller ist, müsste man Messen wenn es einen interessiert.
Relevante Beispiele für Dateiendungen an denen das scheitert: .html und .py.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

@BlackJack
Aus unerklärlichen Gründen hab ich am Ende des letzten Satzes wieder das Wort "schnellsten" verwendet. Meine damit jedoch "besser" und von meiner Sicht aus kleiner und somit besser für den Code.
Aus dem Gedankengang entsprungen weniger Code = schneller.

Hab übrigens etwas weiter an shutil gebastelt und eine Version zum Kopieren einzelner Dateiarten (".txt", ".html", ".png"...) gebastelt.

"src" und "dst" müssen bei als Pfad existieren.
"exten" im Aufruf oder zuvor als Tupel definieren.
Zum Ausführen ins Modul shutil kopieren oder in den zuvor geposteten Code.

Starten mit (speichername bei mir noch edit_shutil), davor natürlich importieren.

Code: Alles auswählen

edit_shutil.treefiles(src, dst, exten=(".txt", ".html"))

Code: Alles auswählen

def treefiles(src, dst, exten, symlinks=False, ignore=None):
    """Copy all files with defined extensions from a directory."""
    names = os.listdir(src)
    try:
        names.remove("$RECYCLE.BIN")
        names.remove("System Volume Information")
    except:
        pass
    
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()
        
    errors = []
    for name in names:
        if name in ignored_names:
            continue
        srcname = os.path.join(src, name)
        dstname = dst
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                treefiles(srcname, dstname, exten, symlinks, ignore)
            else:
                # Will raise a SpecialFileError for unsupported file types
                try:
                    if srcname.endswith(exten):
                        copy2(srcname, dst)
                except:
                    pass
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except Error, err:
            errors.extend(err.args[0])
        except EnvironmentError, why:
            errors.append((srcname, dstname, str(why)))

    try:
        copystat(src, dst)
    except OSError, why:
        if WindowsError is not None and isinstance(why, WindowsError):
            # Copying file access times may fail on Windows
            pass
        else:
            errors.extend((src, dst, str(why)))
    if errors:
        copyonly(srcname, dstname, symlinks, ignore)
        raise Error, errors
Die Möglichkeit die Dateien mit ihren Verzeichnissen zu kopieren kommt auch noch dazu.
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@Dami123: es gibt nicht ohne Grund eine spezielle Funktion um Dateiendungen zu erkennen und aufzuspalten. Unter Unix-ähnlichen Systemen sind Dateien, die mit einem Punkt beginnen versteckt und enthalten meist Konfigurationsdaten für verschieden Programme, das ist aber keine Dateiendung und wird von os.path.splitext auch so erkannt.

Aus Deiner Sicht »bessere« Speziallösungen mögen bei Dir funktionieren, machen aber Dein Modul für andere unbrauchbar.

Du benutzt an zwei Stellen ein nacktes »except« im ersten Fall um wieder eine Speziallösung für Dich abzufangen, im zweiten um stillschweigend jegliche Art von Fehler beim Kopieren zu verstecken. Es können also Dateien beim Kopieren verlorengehen, ohne dass der Nutzer je davon erfahren wird. Damit ist die Funktion in den meisten Fällen unbrauchbar.
Ich sage das hier so hart, weil es beim Programmieren darauf ankommt, gerade die Sonderfälle abzudecken. Eine »tut meistens« reicht einfach nicht.


PS: für den Anfang:

Code: Alles auswählen

def treefiles(src, dst, exten, symlinks=False, ignore=None, ignored_names = ("$RECYCLE.BIN", "System Volume Information")):
    """Copy all files with defined extensions from a directory."""
    names = os.listdir(src)
    ignored_names = set(ignored_names) | set(() if ignore is None else ignore(src, names))
    usw.
Antworten