पुनरावर्ती उप फ़ोल्डर खोज और एक सूची अजगर में फ़ाइलों को वापस


118

मैं एक mainfolder में सबफ़ोल्डर्स के माध्यम से पुनरावृत्ति करने के लिए एक स्क्रिप्ट पर काम कर रहा हूं और एक निश्चित फ़ाइल प्रकार से एक सूची का निर्माण कर रहा हूं। मैं स्क्रिप्ट के साथ एक मुद्दा बना रहा हूं। इसका वर्तमान में निम्नानुसार सेट है

for root, subFolder, files in os.walk(PATH):
    for item in files:
        if item.endswith(".txt") :
            fileNamePath = str(os.path.join(root,subFolder,item))

समस्या यह है कि सबफ़ोल्डर चर को ITEM फ़ाइल स्थित फ़ोल्डर के बजाय सबफ़ोल्डर की सूची में खींच रहा है। मैं पहले सबफ़ोल्डर के लिए लूप चलाने के बारे में सोच रहा था और रास्ते के पहले भाग में शामिल हो गया था, लेकिन मुझे लगा कि यह देखने के लिए कि क्या इससे पहले किसी के पास कोई सुझाव है, तो मैंने आईडी डबल चेक किया। आपकी सहायताके लिए धन्यवाद!

जवाबों:


156

आपको वह dirpathकॉल करना चाहिए जिसका आप उपयोग कर रहे हैं rootdirnamesआपूर्ति की जाती है, तो आप इसे काटना कर सकते हैं, अगर वहाँ फ़ोल्डरों यदि आप नहीं चाहते कि कर रहे हैं os.walkमें recurse करने।

import os
result = [os.path.join(dp, f) for dp, dn, filenames in os.walk(PATH) for f in filenames if os.path.splitext(f)[1] == '.txt']

संपादित करें:

नवीनतम गिरावट के बाद, यह मेरे लिए हुआ कि globविस्तार द्वारा चयन करने के लिए एक बेहतर उपकरण है।

import os
from glob import glob
result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]

इसके अलावा एक जनरेटर संस्करण

from itertools import chain
result = (chain.from_iterable(glob(os.path.join(x[0], '*.txt')) for x in os.walk('.')))

Python 3.4+ के लिए Edit2

from pathlib import Path
result = list(Path(".").rglob("*.[tT][xX][tT]"))

1
'*। [Tt] [xx] [tt]' ग्लोब पैटर्न खोज के मामले को असंवेदनशील बना देगा।
SergiyKolesnikov

@SergiyKolesnikov, धन्यवाद, मैंने इसका उपयोग नीचे के संपादन में किया है। ध्यान दें कि rglobविंडोज प्लेटफॉर्म पर असंवेदनशील है - लेकिन यह कुछ हद तक असंवेदनशील नहीं है।
जॉन ला रोय

1
@ जॉनलारॉय यह globबहुत (यहां पायथन 3.6 के साथ) काम करता है :glob.iglob(os.path.join(real_source_path, '**', '*.[xX][mM][lL]')
SergiyKolesnikov

@ शेरगी: आपका iglobउप-उप फ़ोल्डर या उससे नीचे की फाइलों के लिए काम नहीं करता है। आपको जोड़ने की आवश्यकता है recursive=True
user136036

1
@ user136036, "बेहतर" का अर्थ हमेशा सबसे तेज नहीं होता है। कभी-कभी पठनीयता और स्थिरता भी महत्वपूर्ण होती है।
जॉन ला रूय

111

पायथन 3.5 में परिवर्तित : "**" का उपयोग करके पुनरावर्ती ग्लब्स के लिए समर्थन।

glob.glob()एक नया पुनरावर्ती पैरामीटर मिला है ।

यदि आप प्रत्येक .txtफ़ाइल को my_path(उप-शासकों सहित पुनरावर्ती) प्राप्त करना चाहते हैं :

import glob

files = glob.glob(my_path + '/**/*.txt', recursive=True)

# my_path/     the dir
# **/       every file and dir under my_path
# *.txt     every file that ends with '.txt'

यदि आपको एक पुनरावृत्ति की आवश्यकता है तो आप विकल्प के रूप में इग्लोब का उपयोग कर सकते हैं :

for file in glob.iglob(my_path, recursive=False):
    # ...

1
TypeError: glob () को एक अप्रत्याशित कीवर्ड तर्क 'पुनरावर्ती' मिला
साइबरजैक डेब

1
यह काम करना चाहिए। सुनिश्चित करें कि आप एक संस्करण> = 3.5 का उपयोग करते हैं। मैंने अधिक विस्तार के लिए अपने उत्तर में प्रलेखन के लिए एक लिंक जोड़ा।
रोटेरेटी

यही कारण है कि, मैं 2.7 पर हूँ
साइबरजैकोब

1
क्यों सूची समझ और न सिर्फ files = glob.glob(PATH + '/*/**/*.txt', recursive=True)?
14

ओह! :) यह पूरी तरह से बेमानी है। पता नहीं मुझे किस तरह से लिखना पड़ा। इसका उल्लेख करने के लिए धन्यवाद! मैं इसे ठीक कर दूंगा।
रोटारिटी

20

मैं जॉन ला रोय की सूची का अनुवाद करने के लिए नेस्टेड के लिए समझ जाएगा , बस अगर किसी और को इसे समझने में परेशानी होती है।

result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]

के बराबर होना चाहिए:

import glob

result = []

for x in os.walk(PATH):
    for y in glob.glob(os.path.join(x[0], '*.txt')):
        result.append(y)

यहाँ सूची समझ और कार्यों के लिए प्रलेखन os.walk और glob.glob है


1
इस जवाब ने मेरे लिए पायथॉन 3.7.3 में काम किया। glob.glob(..., recursive=True)और list(Path(dir).glob(...'))नहीं किया।
मिगेलमोरिन

11

यह सबसे तेजी से समाधान मैं के साथ आ सकता है प्रतीत हो रहा है, और है की तुलना में तेजी os.walkऔर एक बहुत किसी भी तुलना में तेजी से globसमाधान

  • यह आपको मूल रूप से बिना किसी लागत के सभी नेस्टेड सबफ़ोल्डर्स की एक सूची भी देगा।
  • आप कई अलग-अलग एक्सटेंशन खोज सकते हैं।
  • तुम भी बदलकर फ़ाइलों के लिए या तो पूर्ण पथ या सिर्फ नाम वापस जाने के लिए चुन सकते हैं f.pathकरने के लिए f.name(सबफ़ोल्डर के लिए इसे बदल नहीं है!)।

Args: dir: str, ext: list
फ़ंक्शन दो सूची देता हैsubfolders, files :।

विस्तृत गति के लिए नीचे देखें।

def run_fast_scandir(dir, ext):    # dir: str, ext: list
    subfolders, files = [], []

    for f in os.scandir(dir):
        if f.is_dir():
            subfolders.append(f.path)
        if f.is_file():
            if os.path.splitext(f.name)[1].lower() in ext:
                files.append(f.path)


    for dir in list(subfolders):
        sf, f = run_fast_scandir(dir, ext)
        subfolders.extend(sf)
        files.extend(f)
    return subfolders, files


subfolders, files = run_fast_scandir(folder, [".jpg"])


गति विश्लेषण

विभिन्न तरीकों के लिए सभी सबफ़ोल्डर्स और मुख्य फ़ोल्डर के अंदर एक विशिष्ट फ़ाइल एक्सटेंशन के साथ सभी फाइलें प्राप्त करने के लिए।

tl; dr:
- fast_scandirस्पष्ट रूप से जीतता है और os.walk को छोड़कर अन्य सभी समाधानों की तुलना में दोगुना है।
- os.walkदूसरे स्थान पर है धीमे धीमे।
- उपयोग globकरने से प्रक्रिया बहुत धीमी हो जाएगी।
- परिणामों में से कोई भी प्राकृतिक छँटाई का उपयोग नहीं करता है । इसका मतलब है कि परिणाम इस प्रकार होंगे: 1, 10, 2. प्राकृतिक छंटाई (1, 2, 10) प्राप्त करने के लिए, कृपया https://stackoverflow.com/a/48030307/2441026 पर एक नज़र डालें।


परिणाम:

fast_scandir    took  499 ms. Found files: 16596. Found subfolders: 439
os.walk         took  589 ms. Found files: 16596
find_files      took  919 ms. Found files: 16596
glob.iglob      took  998 ms. Found files: 16596
glob.glob       took 1002 ms. Found files: 16596
pathlib.rglob   took 1041 ms. Found files: 16596
os.walk-glob    took 1043 ms. Found files: 16596

टेस्ट W7x64, पायथन 3.8.1, 20 रन के साथ किया गया। 439 (आंशिक रूप से नेस्टेड) ​​सबफ़ोल्डर में 16596 फाइलें।
find_filesसे है https://stackoverflow.com/a/45646357/2441026 और आप कई एक्सटेंशन के लिए खोज करने देता है।
fast_scandirस्वयं द्वारा लिखा गया था और वह सबफ़ोल्डर्स की सूची भी लौटाएगा। आप इसे खोज करने के लिए एक्सटेंशन की एक सूची दे सकते हैं (मैंने एक प्रविष्टि के साथ एक सूची का परीक्षण किया जिसमें कोई सरल if ... == ".jpg"और कोई महत्वपूर्ण अंतर नहीं था)।


# -*- coding: utf-8 -*-
# Python 3


import time
import os
from glob import glob, iglob
from pathlib import Path


directory = r"<folder>"
RUNS = 20


def run_os_walk():
    a = time.time_ns()
    for i in range(RUNS):
        fu = [os.path.join(dp, f) for dp, dn, filenames in os.walk(directory) for f in filenames if
                  os.path.splitext(f)[1].lower() == '.jpg']
    print(f"os.walk\t\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_os_walk_glob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.jpg'))]
    print(f"os.walk-glob\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_glob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = glob(os.path.join(directory, '**', '*.jpg'), recursive=True)
    print(f"glob.glob\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_iglob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = list(iglob(os.path.join(directory, '**', '*.jpg'), recursive=True))
    print(f"glob.iglob\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_pathlib_rglob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = list(Path(directory).rglob("*.jpg"))
    print(f"pathlib.rglob\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def find_files(files, dirs=[], extensions=[]):
    # https://stackoverflow.com/a/45646357/2441026

    new_dirs = []
    for d in dirs:
        try:
            new_dirs += [ os.path.join(d, f) for f in os.listdir(d) ]
        except OSError:
            if os.path.splitext(d)[1].lower() in extensions:
                files.append(d)

    if new_dirs:
        find_files(files, new_dirs, extensions )
    else:
        return


def run_fast_scandir(dir, ext):    # dir: str, ext: list
    # https://stackoverflow.com/a/59803793/2441026

    subfolders, files = [], []

    for f in os.scandir(dir):
        if f.is_dir():
            subfolders.append(f.path)
        if f.is_file():
            if os.path.splitext(f.name)[1].lower() in ext:
                files.append(f.path)


    for dir in list(subfolders):
        sf, f = run_fast_scandir(dir, ext)
        subfolders.extend(sf)
        files.extend(f)
    return subfolders, files



if __name__ == '__main__':
    run_os_walk()
    run_os_walk_glob()
    run_glob()
    run_iglob()
    run_pathlib_rglob()


    a = time.time_ns()
    for i in range(RUNS):
        files = []
        find_files(files, dirs=[directory], extensions=[".jpg"])
    print(f"find_files\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(files)}")


    a = time.time_ns()
    for i in range(RUNS):
        subf, files = run_fast_scandir(directory, [".jpg"])
    print(f"fast_scandir\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(files)}. Found subfolders: {len(subf)}")

10

नई pathlibलाइब्रेरी इसे एक पंक्ति में सरल बनाती है:

from pathlib import Path
result = list(Path(PATH).glob('**/*.txt'))

आप जनरेटर संस्करण का उपयोग भी कर सकते हैं:

from pathlib import Path
for file in Path(PATH).glob('**/*.txt'):
    pass

यह Pathऑब्जेक्ट्स लौटाता है, जिसका उपयोग आप किसी भी चीज़ के लिए कर सकते हैं, या एक स्ट्रिंग के रूप में फ़ाइल नाम प्राप्त कर सकते हैं file.name


6

इसका सबसे पैथोनिक उत्तर नहीं है, लेकिन मैं इसे यहाँ मज़े के लिए रखूँगा क्योंकि यह पुनरावृत्ति में एक साफ सबक है

def find_files( files, dirs=[], extensions=[]):
    new_dirs = []
    for d in dirs:
        try:
            new_dirs += [ os.path.join(d, f) for f in os.listdir(d) ]
        except OSError:
            if os.path.splitext(d)[1] in extensions:
                files.append(d)

    if new_dirs:
        find_files(files, new_dirs, extensions )
    else:
        return

मेरी मशीन पर मेरे पास दो फ़ोल्डर हैं, rootऔरroot2

mender@multivax ]ls -R root root2
root:
temp1 temp2

root/temp1:
temp1.1 temp1.2

root/temp1/temp1.1:
f1.mid

root/temp1/temp1.2:
f.mi  f.mid

root/temp2:
tmp.mid

root2:
dummie.txt temp3

root2/temp3:
song.mid

आइए कहते हैं कि मैं इन सभी निर्देशिकाओं में सभी .txtऔर सभी .midफाइलें ढूंढना चाहता हूं , फिर मैं बस कर सकता हूं

files = []
find_files( files, dirs=['root','root2'], extensions=['.mid','.txt'] )
print(files)

#['root2/dummie.txt',
# 'root/temp2/tmp.mid',
# 'root2/temp3/song.mid',
# 'root/temp1/temp1.1/f1.mid',
# 'root/temp1/temp1.2/f.mid']

4

पायथन 3.5 में नया है, इसलिए यह पायथन 2.7 पर काम नहीं करेगा। यहाँ उदाहरण है कि rस्ट्रिंग्स का उपयोग किया जाता है, ताकि आपको केवल विन, लिन, ...

import glob

mypath=r"C:\Users\dj\Desktop\nba"

files = glob.glob(mypath + r'\**\*.py', recursive=True)
# print(files) # as list
for f in files:
    print(f) # nice looking single line per file

नोट: यह सभी फाइलों को सूचीबद्ध करेगा, चाहे वह कितनी भी गहरी हो।


3

आप इसे पूर्ण पथ फ़ाइलों की सूची वापस करने के लिए इस तरह से कर सकते हैं।

def list_files_recursive(path):
    """
    Function that receives as a parameter a directory path
    :return list_: File List and Its Absolute Paths
    """

    import os

    files = []

    # r = root, d = directories, f = files
    for r, d, f in os.walk(path):
        for file in f:
            files.append(os.path.join(r, file))

    lst = [file for file in files]
    return lst


if __name__ == '__main__':

    result = list_files_recursive('/tmp')
    print(result)

3

यदि आपको अतिरिक्त प्रकाश पुस्तकालय स्थापित करने में कोई आपत्ति नहीं है, तो आप यह कर सकते हैं:

pip install plazy

उपयोग:

import plazy

txt_filter = lambda x : True if x.endswith('.txt') else False
files = plazy.list_files(root='data', filter_func=txt_filter, is_include_root=True)

परिणाम कुछ इस तरह दिखना चाहिए:

['data/a.txt', 'data/b.txt', 'data/sub_dir/c.txt']

यह पायथन 2.7 और पायथन 3 दोनों पर काम करता है।

गिथब: https://github.com/kyzas/plazy#list-files

अस्वीकरण: मैं एक लेखक हूँ plazy


1

यह फ़ंक्शन पुनरावर्ती रूप से केवल फ़ाइलों को एक सूची में रखेगा। आशा है कि आप यह करेंगे

import os


def ls_files(dir):
    files = list()
    for item in os.listdir(dir):
        abspath = os.path.join(dir, item)
        try:
            if os.path.isdir(abspath):
                files = files + ls_files(abspath)
            else:
                files.append(abspath)
        except FileNotFoundError as err:
            print('invalid directory\n', 'Error: ', err)
    return files

0

आपका मूल समाधान लगभग बहुत सही था, लेकिन चर "रूट" को गतिशील रूप से अद्यतन किया जाता है क्योंकि यह पुनरावर्ती पथ पर होता है। os.walk () एक पुनरावर्ती जनरेटर है। (रूट, सबफ़ोल्डर, फ़ाइलें) का प्रत्येक टपल सेट एक विशिष्ट रूट के लिए होता है जिस तरह से आप इसे सेटअप करते हैं।

अर्थात

root = 'C:\\'
subFolder = ['Users', 'ProgramFiles', 'ProgramFiles (x86)', 'Windows', ...]
files = ['foo1.txt', 'foo2.txt', 'foo3.txt', ...]

root = 'C:\\Users\\'
subFolder = ['UserAccount1', 'UserAccount2', ...]
files = ['bar1.txt', 'bar2.txt', 'bar3.txt', ...]

...

मैंने एक पूरी सूची मुद्रित करने के लिए आपके कोड के लिए एक मामूली ट्विक बनाया।

import os
for root, subFolder, files in os.walk(PATH):
    for item in files:
        if item.endswith(".txt") :
            fileNamePath = str(os.path.join(root,item))
            print(fileNamePath)

उम्मीद है की यह मदद करेगा!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.