如何进行递归子文件夹搜索并返回列表中的文件?

2025-01-09 08:46:00
admin
原创
87
摘要:问题描述:我正在编写一个脚本,用于递归遍历主文件夹中的子文件夹并根据特定文件类型构建列表。我在使用该脚本时遇到了问题。当前设置如下:for root, subFolder, files in os.walk(PATH): for item in files: if item.endsw...

问题描述:

我正在编写一个脚本,用于递归遍历主文件夹中的子文件夹并根据特定文件类型构建列表。我在使用该脚本时遇到了问题。当前设置如下:

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

问题是变量subFolder拉入的是子文件夹列表,而不是 ITEM 文件所在的文件夹。我之前考虑过对子文件夹运行一个 for 循环,然后加入路径的第一部分,但我想在那之前再检查一下是否有人有什么建议。


解决方案 1:

您应该使用dirpath您调用的rootdirnames提供了,因此如果您不希望递归到某些文件夹,则可以对其进行修剪os.walk

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

解决方案 2:

在Python 3.5中更改:支持使用“**”的递归 glob。

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'

如果你需要迭代器,你可以使用iglob作为替代方案:

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

解决方案 3:

这似乎是我能想到的最快的解决方案,并且比任何解决方案都快os.walk,而且快得多glob

  • 它还将免费为您提供所有嵌套子文件夹的列表。

  • 您可以搜索几种不同的扩展。

  • 您还可以选择返回完整路径或仅返回文件的名称,方法是更改f.path​​为f.name(不要更改子文件夹!)。

参数: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"])

如果您需要文件大小,您也可以创建一个sizes列表并添加f.stat().st_size如下内容来显示 MiB:

sizes.append(f"{f.stat().st_size/1024/1024:.0f} MiB")

速度分析

获取所有子文件夹和主文件夹内具有特定文件扩展名的所有文件的各种方法。

总结:

  • 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

更新时间:2022-07-20(Py3.10.1正在寻找*.pdf

glob.iglob      took 132 ms. Found files: 9999
glob.glob       took 134 ms. Found files: 9999
fast_scandir    took 331 ms. Found files: 9999. Found subfolders: 9330
os.walk         took 695 ms. Found files: 9999
pathlib.rglob   took 828 ms. Found files: 9999
find_files      took 949 ms. Found files: 9999
os.walk-glob    took 1242 ms. Found files: 9999

使用 W7x64、Python 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            took {(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    took {(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        took {(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        took {(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    took {(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        took {(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    took {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(files)}. Found subfolders: {len(subf)}")

解决方案 4:

我将把John La Rooy 的列表推导翻译为嵌套的 for,以防其他人难以理解。

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

应等同于:

import glob
import os

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的文档。

解决方案 5:

新的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:

您的原始解决方案几乎是正确的,但变量“root”在递归路径中会动态更新。os.walk() 是一个递归生成器。每个元组集 (root、subFolder、files) 都针对特定的根,就像您设置的那样。

IE

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)

希望这有帮助!

编辑:(根据反馈)

OP 误解/错误标记了 subFolder 变量,因为它实际上是"root" 中的所有子文件夹。因此,OP,您正在尝试执行 os.path.join(str, list, str),这可能不会像您预期的那样工作。

为了增加清晰度,您可以尝试以下标签方案:

import os
for current_dir_path, current_subdirs, current_files in os.walk(RECURSIVE_ROOT):
    for aFile in current_files:
        if aFile.endswith(".txt") :
            txt_file_path = str(os.path.join(current_dir_path, aFile))
            print(txt_file_path)

解决方案 7:

这不是最符合 Python 风格的答案,但我会把它放在这里以方便取乐,因为它是递归的一个简洁的教训

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

我的机器上有两个文件夹rootroot2

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

解决方案 8:

您可以按照这种方式返回绝对路径文件的列表。

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

    return files


if __name__ == '__main__':

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

解决方案 9:

递归是 Python 3.5 中的新功能,因此它不适用于 Python 2.7。下面是使用r字符串的示例,因此您只需在 Win、Lin 上提供路径即可……

import glob

mypath=r"C:UsersdjDesktop
ba"

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

注意:它将列出所有文件,无论深度如何。

解决方案 10:

如果您不介意安装额外的灯光库,您可以这样做:

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

它适用于 Python 2.7 和 Python 3。

Github:https ://github.com/kyzas/plazy#list-files

免责声明:我是的作者plazy

解决方案 11:

此函数将递归地将文件放入列表中。

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
', 'Error: ', err)
    return files

解决方案 12:

您可以使用 glob 模块中的“递归”设置来搜索子目录

例如:

import glob
glob.glob('//Mypath/folder/**/*',recursive = True)

第二行将返回该文件夹位置的子目录内的所有文件(注意,您需要在文件夹字符串末尾加上“*/”字符串才能执行此操作。)

如果你特别想在子目录深处查找文本文件,你可以使用

glob.glob('//Mypath/folder/**/*.txt',recursive = True)

解决方案 13:

一种最简单最基本的方法:

import os
for parent_path, _, filenames in os.walk('.'):
    for f in filenames:
        print(os.path.join(parent_path, f))
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用