ค้นหาไฟล์ใน python


113

ฉันมีไฟล์ที่อาจอยู่คนละที่ในเครื่องของผู้ใช้แต่ละคน มีวิธีดำเนินการค้นหาไฟล์หรือไม่ วิธีที่ฉันสามารถส่งชื่อไฟล์และแผนผังไดเร็กทอรีเพื่อค้นหาได้?


ดูโมดูลระบบปฏิบัติการสำหรับ os.walk หรือ os.listdir ดูคำถามนี้ด้วยstackoverflow.com/questions/229186/…สำหรับโค้ดตัวอย่าง
Martin Beckett

คำตอบ:


256

os.walkคือคำตอบซึ่งจะพบคู่แรก:

import os

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

และจะพบรายการที่ตรงกันทั้งหมด:

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

และจะตรงกับรูปแบบ:

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
โปรดทราบว่าตัวอย่างเหล่านี้จะพบเฉพาะไฟล์ไม่ใช่ไดเร็กทอรีที่มีชื่อเดียวกัน หากคุณต้องการค้นหาวัตถุใด ๆในไดเรกทอรีที่มีชื่อนั้นคุณอาจต้องการใช้if name in file or name in dirs
Mark E. Hamilton

9
ระวังตัวพิมพ์เล็กและใหญ่ for name in files:จะล้มเหลวในการค้นหาsuper-photo.jpgเมื่อsuper-photo.JPGอยู่ในระบบไฟล์ (หนึ่งชั่วโมงในชีวิตของฉันฉันต้องการกลับมา ;-) การแก้ไขที่ค่อนข้างยุ่งคือif str.lower(name) in [x.lower() for x in files]
matt wilkie

แล้วการใช้ผลตอบแทนแทนการจัดทำรายการผลลัพธ์ล่ะ? ..... if fnmatch.fnmatch (name, pattern): yield os.path.join (root, name)
Berci

โปรดพิจารณาอัปเดตคำตอบของคุณสำหรับ Python 3.x primitives
Dima Tisnek

1
รายการ Comprehention สามารถแทนที่ฟังก์ชันได้เช่น find_all: res = [os.path.join (root, name) สำหรับ root, dirs, files ใน os.walk (path) if name in files]
Nir

23

ฉันใช้เวอร์ชันos.walkและบนไดเร็กทอรีที่ใหญ่ขึ้นเวลาประมาณ 3.5 วินาที ฉันลองวิธีแก้ปัญหาแบบสุ่มสองวิธีโดยไม่มีการปรับปรุงที่ดีเยี่ยมจากนั้นก็ทำ:

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

แม้ว่าจะเป็น POSIX เท่านั้น แต่ฉันมีเวลา 0.25 วินาที

จากสิ่งนี้ฉันเชื่อว่าเป็นไปได้ทั้งหมดที่จะเพิ่มประสิทธิภาพการค้นหาทั้งหมดในรูปแบบที่ไม่ขึ้นกับแพลตฟอร์ม แต่นี่คือจุดที่ฉันหยุดการวิจัย


6

หากคุณใช้ Python บน Ubuntu และคุณต้องการให้มันทำงานบน Ubuntu เท่านั้นวิธีที่เร็วกว่านั้นคือการใช้locateโปรแกรมของเทอร์มินัลเช่นนี้

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_resultsเป็นlistเส้นทางของไฟล์ที่แน่นอน เร็วกว่าวิธีการด้านบน 10,000 เท่าและสำหรับการค้นหาหนึ่งครั้งที่ฉันทำมันเร็วกว่า ~ 72,000 เท่า


5

ใน Python 3.4 หรือใหม่กว่าคุณสามารถใช้ pathlib เพื่อทำการ globbing ซ้ำได้:

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

อ้างอิง: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob

ใน Python 3.5 หรือใหม่กว่าคุณสามารถหมุนวนซ้ำได้เช่นนี้:

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

อ้างอิง: https://docs.python.org/3/library/glob.html#glob.glob


3

สำหรับการค้นหาที่รวดเร็วและไม่ขึ้นกับระบบปฏิบัติการให้ใช้ scandir

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

อ่านhttp://bugs.python.org/issue11406เพื่อดูรายละเอียดว่าทำไม


7
โดยเฉพาะใช้scandir.walk()ต่อคำตอบของ @ Nadia โปรดทราบว่าหากคุณใช้ Python 3.5+ os.walk()มีscandir.walk()speedups อยู่แล้ว นอกจากนี้PEP 471อาจเป็นเอกสารที่ดีกว่าในการอ่านข้อมูลมากกว่าปัญหานั้น
Ben Hoyt

3

หากคุณกำลังทำงานกับ Python 2 คุณมีปัญหากับการเรียกซ้ำแบบไม่สิ้นสุดบน windows ที่เกิดจาก symlink ที่อ้างอิงตัวเอง

สคริปต์นี้จะหลีกเลี่ยงการติดตามสิ่งเหล่านั้น โปรดทราบว่านี่เป็นหน้าต่างเฉพาะ !

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

ส่งคืนรายการที่มีเส้นทางทั้งหมดที่ชี้ไปยังไฟล์ในรายการชื่อไฟล์ การใช้งาน:

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

2

ด้านล่างนี้เราใช้อาร์กิวเมนต์บูลีน "first" เพื่อสลับระหว่างการจับคู่ครั้งแรกและการจับคู่ทั้งหมด (ค่าเริ่มต้นซึ่งเทียบเท่ากับ "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

คำตอบนั้นคล้ายกับคำตอบที่มีอยู่ แต่ได้รับการปรับให้เหมาะสมเล็กน้อย

คุณจึงสามารถค้นหาไฟล์หรือโฟลเดอร์ตามรูปแบบ:

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

ไม่ว่าจะโดยสตริงย่อย:

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
    )

หรือใช้เพรดิเคต:

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

เพื่อค้นหาเฉพาะไฟล์หรือเฉพาะโฟลเดอร์ - แทนที่ "dirs + files" เช่นด้วย "dirs" หรือเฉพาะ "files" ขึ้นอยู่กับสิ่งที่คุณต้องการ

ความนับถือ.


0

คำตอบของ SARose ใช้ได้ผลสำหรับฉันจนกว่าฉันจะอัปเดตจาก Ubuntu 20.04 LTS การเปลี่ยนแปลงเล็กน้อยที่ฉันทำกับรหัสของเขาทำให้ใช้งานได้กับ Ubuntu รุ่นล่าสุด

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.