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

时间:2013-02-10 13:27:02

标签: python filesystems glob fnmatch

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

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

但是这也无法访问子文件夹。有谁知道如何使用相同的命令来访问子文件夹?

13 个答案:

答案 0 :(得分:125)

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

答案 1 :(得分:17)

要查找直接子目录中的文件:

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

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

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

两个函数调用返回列表。您可以使用glob.iglob()逐个返回路径。或use 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

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

答案 2 :(得分:17)

glob2包支持通配符并且速度相当快

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

在我的笔记本电脑上,匹配>60,000 file paths大约需要2秒钟。

答案 3 :(得分:8)

您可以在Python 2.6中使用Formic

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

披露 - 我是此套餐的作者。

答案 4 :(得分:3)

这是一个改编版本,可在不使用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

所以如果你有以下的目录结构

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模式匹配整个文件名本身,而不仅仅是文件名。

答案 5 :(得分:2)

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

不适用于所有情况,而是使用glob2

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

答案 6 :(得分:2)

如果你可以安装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\\**\\**")  

答案 7 :(得分:2)

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

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

答案 8 :(得分:1)

在这个话题上有很多困惑。让我看看是否可以澄清它(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.

答案 9 :(得分:0)

正如Martijn所指出的,glob只能通过Python 3.5中引入的**运算符来实现。由于OP明确要求使用glob模块,因此下面将返回一个行为类似的惰性求值迭代器

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)明确创建它。

答案 10 :(得分:0)

命令rglob将在目录结构的最深子级别进行无限递归。如果您只想深一层,则不要使用它。

我意识到OP正在谈论使用glob.glob。我相信这可以回答意图,即递归搜索所有子文件夹。

rglob函数最近使数据处理算法的速度提高了100倍,该算法使用文件夹结构作为数据读取顺序的固定假设。但是,使用rglob,我们可以一次扫描一次指定父目录中或指定父目录下的所有文件,将它们的名称保存到列表中(超过一百万个文件),然后使用该列表来确定我们要处理的文件未来仅需根据文件命名约定及其所在的文件夹打开文件即可。

答案 11 :(得分:0)

您可以直接从glob模块使用函数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

帮助: Documentation代表os.path.splitextdocumentation代表os.path.basename

答案 12 :(得分:0)

(其他答案中当然会提到第一个选项,此处的目的是表明glob在内部使用os.scandir,并为此提供直接答案)。


使用glob

如前所述,使用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.scandir

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)