পুনরাবৃত্তকারী সাব ফোল্ডার অনুসন্ধান করুন এবং একটি অজগরের ফাইলগুলিতে ফিরুন


117

আমি একটি স্ক্রিপ্টে কাজ করছি মেইনফোল্ডারে পুনরাবৃত্তভাবে সাবফোল্ডারগুলির মধ্য দিয়ে যেতে এবং একটি নির্দিষ্ট ফাইল ধরণের তালিকা তৈরি করতে। স্ক্রিপ্ট নিয়ে আমার সমস্যা হচ্ছে। এটি বর্তমানে নিম্নলিখিত হিসাবে সেট করা হয়েছে

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

সমস্যাটি হ'ল সাবফোল্ডার ভেরিয়েবলটি আইটিইএম ফাইলটি উপস্থিত ফোল্ডারের চেয়ে সাবফোল্ডারগুলির একটি তালিকায় টানছে। আমি আগে সাবফোোল্ডারের জন্য একটি লুপ চালানোর এবং পাথের প্রথম অংশে যোগ দেওয়ার কথা ভাবছিলাম তবে আমি এর আগে আইডি ডাবল চেক পেয়েছিলাম যে এর আগে কারও কোনও পরামর্শ আছে কিনা তা দেখার জন্য। আপনার সাহায্যের জন্য ধন্যবাদ!

উত্তর:


155

আপনি 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('.')))

পাইথন 3.4+ এর জন্য সম্পাদনা 2 2

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

1
'*। [টিটি] [এক্সএক্স] [টিটি]' গ্লোব প্যাটার্ন অনুসন্ধানকে সংবেদনশীল করে তুলবে।
সের্গি কোলেস্নিকভ

@ সার্জি কোলেস্নিকভ, ধন্যবাদ, আমি এটিকে নীচের সম্পাদনায় ব্যবহার করেছি। মনে রাখবেন যে rglobউইন্ডোজ প্ল্যাটফর্মগুলিতে সংবেদনশীল - তবে এটি বাহ্যিক সংবেদনশীল নয়।
জন লা রোয়

1
@ জনলআরউই এটি নিয়েও কাজ globকরে (পাইথন ৩.6 এখানে):glob.iglob(os.path.join(real_source_path, '**', '*.[xX][mM][lL]')
সের্গি কোলেস্নিকভ

@ সার্জি: আপনার iglobসাব-সাব ফোল্ডার বা নীচে ফাইলগুলির জন্য কাজ করে না। আপনি যোগ করা প্রয়োজন recursive=True
ব্যবহারকারী 136036

1
@ ব্যবহারকারী 136036, "আরও ভাল" এর অর্থ সর্বদা দ্রুততম হয় না। কখনও কখনও পাঠযোগ্যতা এবং রক্ষণাবেক্ষণযোগ্যতাও গুরুত্বপূর্ণ।
জন লা রুই

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
এটা কাজ করা উচিত। আপনি কোনও সংস্করণ> = 3.5 ব্যবহার করেছেন তা নিশ্চিত করুন। আমি আরও উত্তরের জন্য আমার উত্তরে ডকুমেন্টেশনে একটি লিঙ্ক যুক্ত করেছি।
রোটেরেটি

সে কারণেই, আমি 2.7 এ আছি
সাইবারজ্যাকব

1
তালিকার বোধগম্যতা কেন শুধু নয় files = glob.glob(PATH + '/*/**/*.txt', recursive=True)?
tobltobs

উপস! :) এটি সম্পূর্ণ অপ্রয়োজনীয়। আমাকে কী লিখতে বাধ্য করলো তা ধারণা নেই। এটি উল্লেখ করার জন্য ধন্যবাদ! আমি এটা ঠিক করব.
রোটারেটি

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
এই উত্তরটি পাইথন ৩.7.৩ এ আমার জন্য কাজ করেছিল। 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দ্বিতীয় স্থানে আস্তে ধীর।
- ব্যবহারের 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

টেস্টগুলি ডাব্লু 7x64, পাইথন 3.8.1, 20 রান দিয়ে হয়েছিল। 439 (আংশিক নেস্টেড) সাবফোল্ডারগুলিতে 16596 ফাইল। https://stackoverflow.com/a/45646357/2441026
find_files থেকে এবং আপনাকে বেশ কয়েকটি এক্সটেনশান অনুসন্ধান করতে দেয়। আমি নিজে লিখেছিলাম এবং সাবফোল্ডারগুলির একটি তালিকাও ফিরিয়ে দেব। আপনি এটি অনুসন্ধানের জন্য এক্সটেনশনের একটি তালিকা দিতে পারেন (আমি একটি সরল প্রবেশের সাথে একটি তালিকা পরীক্ষা করেছি এবং কোনও তাত্পর্যপূর্ণ পার্থক্য ছিল না)।
fast_scandirif ... == ".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)

2

যদি আপনার অতিরিক্ত হালকা লাইব্রেরি ইনস্টল করতে আপত্তি না থাকে তবে আপনি এটি করতে পারেন:

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)

আশাকরি এটা সাহায্য করবে!

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.