我正在编写一个脚本来递归浏览主文件夹中的子文件夹并构建一个特定文件类型的列表。我遇到了脚本问题。目前设定如下
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循环并加入路径的第一部分,但我想我会仔细检查以确定是否有人在此之前有任何建议。谢谢你的帮助!
答案 0 :(得分:125)
您应该使用调用dirpath
的{{1}}。提供了root
,以便在您不希望dirnames
递归的文件夹中修剪它。
os.walk
修改强>
在最新的downvote之后,我发现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'))]
Edit2 for Python 3.4 +
from itertools import chain
result = (chain.from_iterable(glob(os.path.join(x[0], '*.txt')) for x in os.walk('.')))
答案 1 :(得分:66)
在 Python 3.5 中更改:使用“**”支持递归globs。
glob.glob()
获得了新的recursive parameter。
如果您希望获取.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=False):
# ...
答案 2 :(得分:20)
这似乎是我能想到的最快的解决方案,并且比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,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)}")
答案 3 :(得分:13)
我会将John La Rooy's list comprehension翻译成嵌套的,以防万一其他人无法理解它。
result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]
应该相当于:
result = []
for x in os.walk(PATH):
for y in glob(os.path.join(x[0], '*.txt')):
result.append(y)
以下是list comprehension以及os.walk和glob.glob函数的文档。
答案 4 :(得分:4)
您的原始解决方案几乎是正确的,但是变量“ root”会以递归方式动态地更新。 os.walk()是一个递归生成器。每个元组集(根,subFolder,文件)都是按照设置方式针对特定根的。
即
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)
希望这会有所帮助!
答案 5 :(得分:3)
新的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 :(得分: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
答案 7 :(得分:1)
您可以通过这种方式返回绝对路径文件列表。
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)
答案 8 :(得分:1)
您可以使用glob模块中的“递归”设置来搜索子目录
例如:
import glob
glob.glob('//Mypath/folder/**/*',recursive = True)
第二行将返回该文件夹位置的子目录内的所有文件(请注意,您需要在文件夹字符串末尾使用'** / *'字符串)
如果您特别想在子目录中查找文本文件,可以使用
glob.glob('//Mypath/folder/**/*.txt',recursive = True)
答案 9 :(得分:0)
递归是Python 3.5中的新增功能,因此它不适用于Python 2.7。这是使用r
字符串的示例,因此您只需要按Win,Lin,...
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
注意:它将列出所有文件,无论文件应多深。
答案 10 :(得分:0)
如果您不介意安装其他灯库,则可以执行以下操作:
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 :(得分:-1)
最简单最基本的方法:
import os
for parent_path, _, filenames in os.walk('.'):
for f in filenames:
print(os.path.join(parent_path, f))