我正在编写一个脚本,以递归地遍历主文件夹中的子文件夹,并构建一个特定文件类型的列表。我对剧本有点意见。目前设置如下:

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循环,并加入路径的第一部分,但我想我会仔细检查,看看是否有人有任何建议之前。


当前回答

您应该使用称为root的dirpath。提供了dirnames,因此如果有不希望操作的文件夹,您可以删除它。递归入。

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

其他回答

最简单最基本的方法:

import os
for parent_path, _, filenames in os.walk('.'):
    for f in filenames:
        print(os.path.join(parent_path, f))

这不是最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

在我的机器上有两个文件夹,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']

这似乎是我能想出的最快的解决方案,比os更快。走,比任何glob解决方案快得多。

它还会给你一个所有嵌套子文件夹的列表,基本上没有成本。 您可以搜索几个不同的扩展。 您还可以通过将f.path更改为f.name(不要为子文件夹更改它!)来选择返回完整路径或仅返回文件的名称。

参数:dir: str, ext: list。 函数返回两个列表:子文件夹、文件。

请参阅下面的详细速度分析。

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

如果你需要文件大小,你也可以创建一个大小列表并添加f.t at()。st_size用于显示MiB:

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

速度分析

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

tl; diana:

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

更新日期:2022-07-20 (Py 3.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\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)}")

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

例如:

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

第二行将返回该文件夹位置子目录中的所有文件(注意,您需要在文件夹字符串的末尾使用'**/*'字符串来执行此操作)。

如果您特别希望查找子目录深处的文本文件,可以使用

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

我将把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的文档。走着走着。