Suchen Sie eine Datei in Python


109

Ich habe eine Datei, die sich möglicherweise an einer anderen Stelle auf dem Computer jedes Benutzers befindet. Gibt es eine Möglichkeit, eine Suche nach der Datei durchzuführen? Eine Möglichkeit, den Dateinamen und den Verzeichnisbaum für die Suche zu übergeben?


Siehe das OS- Modul für os.walk oder os.listdir. Siehe auch diese Frage stackoverflow.com/questions/229186/… für Beispielcode
Martin Beckett

Antworten:


249

os.walk ist die Antwort, dies wird die erste Übereinstimmung finden:

import os

def find(name, path):
    for root, dirs, files in os.walk(path):
        if name in files:
            return os.path.join(root, name)

Und das findet alle Übereinstimmungen:

def find_all(name, path):
    result = []
    for root, dirs, files in os.walk(path):
        if name in files:
            result.append(os.path.join(root, name))
    return result

Und das passt zu einem Muster:

import os, fnmatch
def find(pattern, path):
    result = []
    for root, dirs, files in os.walk(path):
        for name in files:
            if fnmatch.fnmatch(name, pattern):
                result.append(os.path.join(root, name))
    return result

find('*.txt', '/path/to/dir')

2
Beachten Sie, dass diese Beispiele nur Dateien finden, keine Verzeichnisse mit demselben Namen. Wenn Sie ein Objekt im Verzeichnis mit diesem Namen suchen möchten, das Sie möglicherweise verwenden möchtenif name in file or name in dirs
Mark E. Hamilton,

9
Achten Sie auf die Groß- und Kleinschreibung. for name in files:wird nicht suchen, super-photo.jpgwenn es super-photo.JPGim Dateisystem ist. (eine Stunde meines Lebens würde ich gerne zurück ;-) Etwas chaotisch istif str.lower(name) in [x.lower() for x in files]
matt wilkie

Was ist mit der Verwendung von Yield anstelle der Erstellung der Ergebnisliste? ..... wenn fnmatch.fnmatch (Name, Muster): Ausbeute os.path.join (Wurzel, Name)
Berci

Bitte erwägen Sie, Ihre Antwort auf Python 3.x-
Grundelemente

1
Die Verständnisliste kann die Funktion ersetzen, z. B. find_all: res = [os.path.join (root, name) für root, dirs, Dateien in os.walk (Pfad), wenn Name in Dateien]
Nir

23

Ich habe eine Version von os.walkund in einem größeren Verzeichnis mal ca. 3,5 sek bekommen. Ich habe zwei zufällige Lösungen ohne große Verbesserung ausprobiert und dann einfach:

paths = [line[2:] for line in subprocess.check_output("find . -iname '*.txt'", shell=True).splitlines()]

Während es nur POSIX ist, habe ich 0,25 Sekunden.

Aus diesem Grund glaube ich, dass es durchaus möglich ist, die gesamte Suche plattformunabhängig zu optimieren, aber hier habe ich die Forschung eingestellt.


6

Wenn Sie Python unter Ubuntu verwenden und nur möchten, dass es unter Ubuntu wesentlich schneller funktioniert, verwenden Sie das locateProgramm des Terminals wie folgt .

import subprocess

def find_files(file_name):
    command = ['locate', file_name]

    output = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]
    output = output.decode()

    search_results = output.split('\n')

    return search_results

search_resultsist einer listder absoluten Dateipfade. Dies ist 10.000 Mal schneller als die oben genannten Methoden und für eine Suche, die ich durchgeführt habe, war es ~ 72.000 Mal schneller.


5

In Python 3.4 oder neuer können Sie pathlib verwenden, um rekursives Globbing durchzuführen:

>>> import pathlib
>>> sorted(pathlib.Path('.').glob('**/*.py'))
[PosixPath('build/lib/pathlib.py'),
 PosixPath('docs/conf.py'),
 PosixPath('pathlib.py'),
 PosixPath('setup.py'),
 PosixPath('test_pathlib.py')]

Referenz: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob

In Python 3.5 oder neuer können Sie auch rekursives Globbing wie folgt ausführen:

>>> import glob
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']

Referenz: https://docs.python.org/3/library/glob.html#glob.glob



3

Wenn Sie mit Python 2 arbeiten, haben Sie ein Problem mit der unendlichen Rekursion unter Windows, das durch selbstreferenzierende Symlinks verursacht wird.

Dieses Skript vermeidet es, diesen zu folgen. Beachten Sie, dass dies Windows-spezifisch ist !

import os
from scandir import scandir
import ctypes

def is_sym_link(path):
    # http://stackoverflow.com/a/35915819
    FILE_ATTRIBUTE_REPARSE_POINT = 0x0400
    return os.path.isdir(path) and (ctypes.windll.kernel32.GetFileAttributesW(unicode(path)) & FILE_ATTRIBUTE_REPARSE_POINT)

def find(base, filenames):
    hits = []

    def find_in_dir_subdir(direc):
        content = scandir(direc)
        for entry in content:
            if entry.name in filenames:
                hits.append(os.path.join(direc, entry.name))

            elif entry.is_dir() and not is_sym_link(os.path.join(direc, entry.name)):
                try:
                    find_in_dir_subdir(os.path.join(direc, entry.name))
                except UnicodeDecodeError:
                    print "Could not resolve " + os.path.join(direc, entry.name)
                    continue

    if not os.path.exists(base):
        return
    else:
        find_in_dir_subdir(base)

    return hits

Es wird eine Liste mit allen Pfaden zurückgegeben, die auf Dateien in der Dateinamenliste verweisen. Verwendung:

find("C:\\", ["file1.abc", "file2.abc", "file3.abc", "file4.abc", "file5.abc"])

2

Im Folgenden verwenden wir ein boolesches Argument "first", um zwischen der ersten Übereinstimmung und allen Übereinstimmungen zu wechseln (eine Standardeinstellung, die "find. -Name file" entspricht):

import  os

def find(root, file, first=False):
    for d, subD, f in os.walk(root):
        if file in f:
            print("{0} : {1}".format(file, d))
            if first == True:
                break 

0

Die Antwort ist den bestehenden sehr ähnlich, aber leicht optimiert.

So können Sie alle Dateien oder Ordner nach Muster finden:

def iter_all(pattern, path):
    return (
        os.path.join(root, entry)
        for root, dirs, files in os.walk(path)
        for entry in dirs + files
        if pattern.match(entry)
    )

entweder durch Teilzeichenfolge:

def iter_all(substring, path):
    return (
        os.path.join(root, entry)
        for root, dirs, files in os.walk(path)
        for entry in dirs + files
        if substring in entry
    )

oder mit einem Prädikat:

def iter_all(predicate, path):
    return (
        os.path.join(root, entry)
        for root, dirs, files in os.walk(path)
        for entry in dirs + files
        if predicate(entry)
    )

Um nur Dateien oder nur Ordner zu durchsuchen, ersetzen Sie "dirs + files" beispielsweise durch "dirs" oder nur "files", je nachdem, was Sie benötigen.

Grüße.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.