Datei-finder

Code-Stücke können hier veröffentlicht werden.
Antworten
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

Hi,
Datei-finder (oder Filefinder) ist ein Modul zum POSIX-find ähnlichem finden von Dateien (in kurzform: datei-suchmaschine).

Davon gibt es zwei varianten: Eine rekursive*, und eine non-rekursive Variante.
Hier gehts zum Skript.


*: bedeutet, dass ich dafür os.walk() verwende, was leider ultra-rekursiv ist.
lunar

Der Anspruch ist gewagt. POSIX-find kann weitaus mehr als Dateien gegen reguläre Ausdrücke prüfen.

Die Implementierung ist imho nicht ganz gelungen. Wieso setzt Du nicht einfach ein "re.compile()"-Objekt als "pattern" voraus, sondern baust letztlich die Signatur von "re.compile()" nach? Das Kompilieren des regulären Ausdrucks wäre auch performanter.

Außerdem wäre "re.search()" wohl sinnvoller als "re.match()". Schließlich möchte man meistens einfach nur irgendein Stück des Dateinamens prüfen, und um das Verhalten von "re.match()" zu erreichen, ist es genug, einfach "^" for das Muster zu setzen.

Zudem sind diese Funktionen geradezu prädestiniert für den Einsatz von Generatoren.

Die Rekursivität dagegen ist eigentlich ziemlich egal. Welches Dateisystem hat schon tausend Ebenen.
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

lunar hat geschrieben:Der Anspruch ist gewagt. POSIX-find kann weitaus mehr als Dateien gegen reguläre Ausdrücke prüfen.
Ja.. natürlich kann es das :D Das war auch mehr als vergleich gemeint (ein schlechter, zugegebenermassen).
Die Implementierung ist imho nicht ganz gelungen. Wieso setzt Du nicht einfach ein "re.compile()"-Objekt als "pattern" voraus, sondern baust letztlich die Signatur von "re.compile()" nach? Das Kompilieren des regulären Ausdrucks wäre auch performanter.

Außerdem wäre "re.search()" wohl sinnvoller als "re.match()". Schließlich möchte man meistens einfach nur irgendein Stück des Dateinamens prüfen, und um das Verhalten von "re.match()" zu erreichen, ist es
genug, einfach "^" for das Muster zu setzen.

Zudem sind diese Funktionen geradezu prädestiniert für den Einsatz von Generatoren.
Okay, umgesetzt, siehe hier. danke für den hinweis ;)
Die Rekursivität dagegen ist eigentlich ziemlich egal. Welches Dateisystem hat schon tausend Ebenen.
Ich hätte halt eine Funktion mit variabler rekursion irgendwie praktisch(er) gefunden... vielleicht bau ich sowas ja ein, wenn ich rausfind wie ;)
lunar

Was ist den "variable Rekursion"?
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

Eine, in der man die maximale suchtiefe bestimmen kann (-depth bei find). Sowas wäre schon praktisch ;)
lunar

Ach so. In diesem Fall musst Du "os.walk()" neu implementieren, und die Rekursionstiefe zählen.

Aufgefallen ist mir noch die völlig unsinnige Ausnahmebehandlung. Wieso fängst Du die Ausnahmen ab, nur um sie dann neu zu verpacken? Lass die Ausnahmebehandlung einfach sein, dass ist Sache des Aufrufers. Zumal Du "re.error" an der völlig falschen Stelle abfängst, der Fehler wird bereits bei "re.compile()" ausgelöst.

Apropos "re.compile()": Wenn Du das Muster kompilierst, dann kannst Du auch gleich die Methoden dieses Exemplars verwenden ;) "re.search()" ist dann "repattern.search()".

Allgemein aber würde ich "fileinfo()" komplett streichen. Die Auswahl ist willkürlich und realitätsfern (auf Lesbarkeit testet man durch das Öffnen der Datei). Reiche einfach den absoluten Dateinamen nach oben durch, dann kann der Aufrufer die relevanten Metadaten mittels "os.path.*" trivialerweise selbst abfragen.

Der triviale Rest der Funktion sieht dann so aus:

Code: Alles auswählen

def find(pattern, directory, searchdirs=True):
    for filename in os.listdir(directory):
        fullpath = os.path.join(directory, filename)
        try:
            if (not os.path.isdir(fullpath) or searchdirs):
                if pattern.search(filename):
                    yield fullpath
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

lunar hat geschrieben:Ach so. In diesem Fall musst Du "os.walk()" neu implementieren, und die Rekursionstiefe zählen.
Das meinte ich damit ;) ... Das werd ich die nächsten tage versuchen.
Aufgefallen ist mir noch die völlig unsinnige Ausnahmebehandlung. Wieso fängst Du die Ausnahmen ab, nur um sie dann neu zu verpacken? Lass die Ausnahmebehandlung einfach sein, dass ist Sache des Aufrufers. Zumal Du "re.error" an der völlig falschen Stelle abfängst, der Fehler wird bereits bei "re.compile()" ausgelöst.

Apropos "re.compile()": Wenn Du das Muster kompilierst, dann kannst Du auch gleich die Methoden dieses Exemplars verwenden ;) "re.search()" ist dann "repattern.search()".
Das mit der Ausnahme-behandlung passiert, wenn man gedankenlos text hind-und-her kopiert :D
Die sache RePatternObj.search() wusst ich nicht - ist beides in rev 3db445 gefixt.

Allgemein aber würde ich "fileinfo()" komplett streichen. Die Auswahl ist willkürlich und realitätsfern (auf Lesbarkeit testet man durch das Öffnen der Datei). Reiche einfach den absoluten Dateinamen nach oben durch, dann kann der Aufrufer die relevanten Metadaten mittels "os.path.*" trivialerweise selbst abfragen.
Da stimm ich zu. Ist in der aktuellen revision auch nichtmehr vorhanden ;)
Der triviale Rest der Funktion sieht dann so aus:

Code: Alles auswählen

def find(pattern, directory, searchdirs=True):
    for filename in os.listdir(directory):
        fullpath = os.path.join(directory, filename)
        try:
            if (not os.path.isdir(fullpath) or searchdirs):
                if pattern.search(filename):
                    yield fullpath
Abgesehen davon dass du ein try-except block anfängst aber nicht beendest, wird das pattern an keiner stelle kompiliert (ich vermute mal dass du davon ausgehst dass der user das kompilat selbst übergeben soll). Ich hab das zum teil übernommen, nur wird in meiner Funktion das kompilat selbstständig erstellst, darum muss sich der User keine gedanken mehr machen müssen.
lunar

In "compile_pattern" erstellst Du immer noch Deinen eigenen RegexError. Warum? Der Nutzer sollte einfach direkt "re.error()" abfangen. "re" muss sowieso importiert werden, um an die Konstanten für die Flags zu gelangen.
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

lunar hat geschrieben:In "compile_pattern" erstellst Du immer noch Deinen eigenen RegexError. Warum? Der Nutzer sollte einfach direkt "re.error()" abfangen. "re" muss sowieso importiert werden, um an die Konstanten für die Flags zu gelangen.
fix'd
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

Ich hab jetzt sowas wie os.walk() mit suchtiefen-begrenzung, bin mir nur nicht sicher, ob das auch wirklich funktioniert (oder besser gesagt, ob es vielleicht fehler gibt die ich übersehen hab):

Code: Alles auswählen

import os

def walk(folder, limit, depth=0):
    if depth > limit:
        return
    for filename in os.listdir(folder):
        fullpath = os.path.join(folder, filename)
        if os.path.isdir(fullpath):
            yield fullpath
            for item in walk(fullpath, limit, depth + 1):
                yield item
        else:
            yield fullpath
Antworten