如何使用 glob.glob 模块搜索子文件夹?

我想打开一个文件夹中的一系列子文件夹,找到一些文本文件并打印文本文件的一些行。我用的是这个:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')

但这也不能访问子文件夹。有人知道如何使用相同的命令来访问子文件夹吗?

265451 次浏览

在 Python 3.5和更新的版本中,使用了新的递归 **/功能:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/**/*.txt', recursive=True)

设置 recursive时,后跟路径分隔符的 **匹配0个或多个子目录。

在早期的 Python 版本中,glob.glob()不能递归地列出子目录中的文件。

在这种情况下,我会使用 os.walk()fnmatch.filter()相结合:

import os
import fnmatch


path = 'C:/Users/sam/Desktop/file1'


configfiles = [os.path.join(dirpath, f)
for dirpath, dirnames, files in os.walk(path)
for f in fnmatch.filter(files, '*.txt')]

这将递归地遍历目录,并将所有绝对路径名返回到匹配的 .txt文件。在这种 具体点的情况下,fnmatch.filter()可能是过度的,你也可以使用 .endswith()测试:

import os


path = 'C:/Users/sam/Desktop/file1'


configfiles = [os.path.join(dirpath, f)
for dirpath, dirnames, files in os.walk(path)
for f in files if f.endswith('.txt')]

在直接子目录中查找文件:

configfiles = glob.glob(r'C:\Users\sam\Desktop\*\*.txt')

对于遍历所有子目录的递归版本,可以使用 **并传递 recursive=True 从 Python 3.5开始:

configfiles = glob.glob(r'C:\Users\sam\Desktop\**\*.txt', recursive=True)

两个函数调用都返回列表。你可以使用 glob.iglob()一个一个返回路径。或者使用 使用 pathlib:

from pathlib import Path


path = Path(r'C:\Users\sam\Desktop')
txt_files_only_subdirs = path.glob('*/*.txt')
txt_files_all_recursively = path.rglob('*.txt') # including the current dir

这两种方法都返回迭代器(您可以逐个获取路径)。

您可以在 Python 2.6中使用 虫族

import formic
fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")

披露-我是这个软件包的作者。

Glob2软件包支持通配符,速度相当快

code = '''
import glob2
glob2.glob("files/*/**")
'''
timeit.timeit(code, number=1)

在我的笔记本上,匹配 > 60,000个文件路径大约需要2秒钟。

这里是一个改编版本,支持类似 glob.glob的功能,而不使用 glob2

def find_files(directory, pattern='*'):
if not os.path.exists(directory):
raise ValueError("Directory not found {}".format(directory))


matches = []
for root, dirnames, filenames in os.walk(directory):
for filename in filenames:
full_path = os.path.join(root, filename)
if fnmatch.filter([full_path], pattern):
matches.append(os.path.join(root, filename))
return matches

因此,如果您有下面的 dir 结构

tests/files
├── a0
│   ├── a0.txt
│   ├── a0.yaml
│   └── b0
│       ├── b0.yaml
│       └── b00.yaml
└── a1

你可以这么做

files = utils.find_files('tests/files','**/b0/b*.yaml')
> ['tests/files/a0/b0/b0.yaml', 'tests/files/a0/b0/b00.yaml']

基本上 fnmatch模式匹配整个文件名本身,而不仅仅是文件名。

正如 Martijn 所指出的,globb 只能通过 Python 3.5中引入的 **运算符来完成这项工作。由于 OP 显式要求使用 globb 模块,因此下面将返回一个行为类似的延迟计算迭代器

import os, glob, itertools


configfiles = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.txt'))
for root, dirs, files in os.walk('C:/Users/sam/Desktop/file1/'))

但是请注意,在这种方法中只能在 configfiles上迭代一次。如果您需要一个可以在多个操作中使用的配置文件的实际列表,那么您必须使用 list(configfiles)显式地创建这个列表。

configfiles = glob.glob('C:/Users/sam/Desktop/**/*.txt")

不能适用于所有情况,请使用 glob2

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")

如果你能安装 glob2软件包..。

import glob2
filenames = glob2.glob("C:\\top_directory\\**\\*.ext")  # Where ext is a specific file extension
folders = glob2.glob("C:\\top_directory\\**\\")

所有文件名和文件夹:

all_ff = glob2.glob("C:\\top_directory\\**\\**")

如果您正在运行 Python 3.4 + ,则可以使用 pathlib模块。Path.glob()方法支持 **模式,这意味着“这个目录和所有子目录,递归”。它返回一个生成器,为所有匹配的文件生成 Path对象。

from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")

关于这个主题有很多混淆,让我看看是否可以澄清一下(Python 3.7) :

  1. glob.glob('*.txt') :匹配所有以’. txt’结尾的工作目录文件
  2. glob.glob('*/*.txt') :与1相同
  3. glob.glob('**/*.txt') :匹配 仅限于直接子目录中以’. txt’结尾的所有文件,但不匹配工作目录中的文件
  4. glob.glob('*.txt',recursive=True) :与1相同
  5. glob.glob('*/*.txt',recursive=True) :与3相同
  6. glob.glob('**/*.txt',recursive=True):匹配工作目录和所有子目录中以’. txt’结尾的所有文件

所以最好始终指定 recursive=True.

命令 rglob将在目录结构的最深层次执行无限递归。但是,如果您只想要一个级别的深度,那么不要使用它。

我知道 OP 说的是使用 glob.globb。但是,我相信这回答了递归搜索所有子文件夹的意图。

rglob函数最近使数据处理算法的速度提高了100倍,该算法使用文件夹结构作为数据读取顺序的固定假设。然而,使用 rglob,我们能够对指定父目录下的所有文件进行一次扫描,将它们的名字保存到一个列表中(超过100万个文件) ,然后根据文件命名约定和它们所在的文件夹,使用该列表来确定我们在未来的任何时候需要打开哪些文件。

您可以直接从 globb 模块使用函数 glob.glob()glob.iglob()来递归地从目录/文件和子目录/子文件中检索路径。

句法:

glob.glob(pathname, *, recursive=False) # pathname = '/path/to/the/directory' or subdirectory
glob.iglob(pathname, *, recursive=False)

在你的例子中,可以这样写:


import glob
import os


configfiles = [f for f in glob.glob("C:/Users/sam/Desktop/*.txt")]


for f in configfiles:
print(f'Filename with path: {f}')
print(f'Only filename: {os.path.basename(f)}')
print(f'Filename without extensions: {os.path.splitext(os.path.basename(f))[0]}')

产出:

Filename with path: C:/Users/sam/Desktop/test_file.txt
Only filename: test_file.txt
Filename without extensions: test_file

帮助: os.path.splitext的文档 os.path.basename文件的文档 。

(第一个选项当然会在其他答案中提到,这里的目标是展示 globb 在内部使用 os.scandir,并提供一个直接的答案)。


用水珠

如前所述,对于 Python 3.5 + ,这很简单:

import glob
for f in glob.glob('d:/temp/**/*', recursive=True):
print(f)


#d:\temp\New folder
#d:\temp\New Text Document - Copy.txt
#d:\temp\New folder\New Text Document - Copy.txt
#d:\temp\New folder\New Text Document.txt

使用 pathlib

from pathlib import Path
for f in Path('d:/temp').glob('**/*'):
print(f)

使用 os.canddir

os.scandirglob在内部完成的。下面是如何利用 yield直接完成的:

def listpath(path):
for f in os.scandir(path):
f2 = os.path.join(path, f)
if os.path.isdir(f):
yield f2
yield from listpath(f2)
else:
yield f2


for f in listpath('d:\\temp'):
print(f)