Tìm tệp trong python


110

Tôi có một tệp có thể ở một nơi khác trên máy của mỗi người dùng. Có cách nào để thực hiện tìm kiếm tệp không? Một cách để tôi có thể chuyển tên tệp và cây thư mục để tìm kiếm?


Xem mô-đun hệ điều hành cho os.walk hoặc os.listdir Xem thêm câu hỏi này stackoverflow.com/questions/229186/… để biết mã mẫu
Martin Beckett

Câu trả lời:


251

os.walk là câu trả lời, điều này sẽ tìm thấy kết quả phù hợp đầu tiên:

import os

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

Và điều này sẽ tìm thấy tất cả các trận đấu:

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

Và điều này sẽ phù hợp với một mẫu:

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
Lưu ý rằng những ví dụ này sẽ chỉ tìm thấy các tệp chứ không phải các thư mục có cùng tên. Nếu bạn muốn tìm bất kỳ đối tượng trong thư mục với tên mà bạn có thể muốn sử dụngif name in file or name in dirs
Mark E. Hamilton

9
Cẩn thận với phân biệt chữ hoa chữ thường. for name in files:sẽ không tìm kiếm được super-photo.jpgkhi nó super-photo.JPGnằm trong hệ thống tệp. (một giờ trong đời, tôi muốn quay lại ;-) Cách sửa chữa hơi lộn xộn làif str.lower(name) in [x.lower() for x in files]
matt wilkie

Điều gì về việc sử dụng lợi nhuận thay vì chuẩn bị danh sách kết quả? ..... nếu fnmatch.fnmatch (tên, mẫu): năng suất os.path.join (gốc, tên)
Berci

Vui lòng xem xét cập nhật câu trả lời của bạn cho các nguyên thủy Python 3.x
Dima Tisnek

1
Danh sách tổng hợp có thể thay thế hàm, ví dụ: find_all: res = [os.path.join (root, name) cho root, dirs, các tệp trong os.walk (đường dẫn) nếu tên trong tệp]
Nir

23

Tôi đã sử dụng phiên bản của os.walkvà trên một thư mục lớn hơn có thời gian khoảng 3,5 giây. Tôi đã thử hai giải pháp ngẫu nhiên mà không có cải tiến lớn, sau đó chỉ làm:

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

Trong khi nó chỉ dành cho POSIX, tôi đã có 0,25 giây.

Từ điều này, tôi tin rằng hoàn toàn có thể tối ưu hóa toàn bộ tìm kiếm theo cách độc lập với nền tảng, nhưng đây là lúc tôi dừng nghiên cứu.


6

Nếu bạn đang sử dụng Python trên Ubuntu và bạn chỉ muốn nó hoạt động trên Ubuntu thì một cách nhanh hơn đáng kể là sử dụng locatechương trình của thiết bị đầu cuối như thế này.

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_resultslà một listtrong những đường dẫn tệp tuyệt đối. Cách này nhanh hơn 10.000 lần so với các phương pháp trên và đối với một lần tìm kiếm tôi đã thực hiện nhanh hơn ~ 72.000 lần.


5

Trong Python 3.4 hoặc mới hơn, bạn có thể sử dụng pathlib để thực hiện thao tác lặp lại đệ quy:

>>> 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')]

Tham khảo: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob

Trong Python 3.5 hoặc mới hơn, bạn cũng có thể thực hiện thao tác lặp lại đệ quy như sau:

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

Tham khảo: https://docs.python.org/3/library/glob.html#glob.glob


3

Để tìm kiếm nhanh chóng, không phụ thuộc vào hệ điều hành, hãy sử dụng scandir

https://github.com/benhoyt/scandir/#readme

Đọc http://bugs.python.org/issue11406 để biết chi tiết tại sao.


7
Cụ thể, hãy sử dụng scandir.walk()câu trả lời của per @ Nadia. Lưu ý rằng nếu bạn đang sử dụng Python 3.5+, os.walk()thì scandir.walk()tốc độ đã tăng lên. Ngoài ra, PEP 471 có lẽ là một tài liệu tốt hơn để đọc để biết thông tin hơn là vấn đề đó.
Ben Hoyt

3

Nếu bạn đang làm việc với Python 2, bạn gặp sự cố với đệ quy vô hạn trên các cửa sổ do các liên kết tượng trưng tự giới thiệu.

Tập lệnh này sẽ tránh làm theo những điều đó. Lưu ý rằng đây là cửa sổ cụ thể !

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

Nó trả về một danh sách với tất cả các đường dẫn trỏ đến các tệp trong danh sách tên tệp. Sử dụng:

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

2

Dưới đây chúng tôi sử dụng đối số boolean "đầu tiên" để chuyển đổi giữa kết hợp đầu tiên và tất cả các kết quả phù hợp (mặc định tương đương với "find. -Name file"):

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

Câu trả lời rất giống với những cái hiện có, nhưng được tối ưu hóa một chút.

Vì vậy, bạn có thể tìm thấy bất kỳ tệp hoặc thư mục nào theo mẫu:

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)
    )

hoặc bằng chuỗi con:

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
    )

hoặc sử dụng một vị ngữ:

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)
    )

để chỉ tìm kiếm tệp hoặc chỉ thư mục - thay thế “dirs + tệp”, ví dụ: chỉ bằng “dirs” hoặc chỉ “tệp”, tùy thuộc vào những gì bạn cần.

Trân trọng.


0

Câu trả lời của SARose đã phù hợp với tôi cho đến khi tôi cập nhật từ Ubuntu 20.04 LTS. Thay đổi nhỏ mà tôi đã thực hiện đối với mã của anh ấy khiến nó hoạt động trên bản phát hành Ubuntu mới nhất.

import subprocess

def find_files(file_name):
    file_name = 'chromedriver'
    command = ['locate'+ ' ' + file_name]
    output = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]
    output = output.decode()
    search_results = output.split('\n')
    return search_results
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.