Python glob 多种文件类型

Posted

技术标签:

【中文标题】Python glob 多种文件类型【英文标题】:Python glob multiple filetypes 【发布时间】:2011-06-01 21:36:06 【问题描述】:

有没有更好的方法在 python 中使用 glob.glob 来获取多个文件类型的列表,例如 .txt、.mdown 和 .markdown?现在我有这样的东西:

projectFiles1 = glob.glob( os.path.join(projectDir, '*.txt') )
projectFiles2 = glob.glob( os.path.join(projectDir, '*.mdown') )
projectFiles3 = glob.glob( os.path.join(projectDir, '*.markdown') )

【问题讨论】:

非常相关:***.com/q/48181073/880783 为什么不main_file = projectFiles1 + projectFiles2 + projectFiles3 ?这也将通过连接导致一个包含所有类型的主列表 从未见过文件 *.mdown ..;) 【参考方案1】:

也许有更好的方法,但是怎么样:

import glob
types = ('*.pdf', '*.cpp') # the tuple of file types
files_grabbed = []
for files in types:
    files_grabbed.extend(glob.glob(files))

# files_grabbed is the list of pdf and cpp files

也许还有其他方法,所以请等待其他人提出更好的答案。

【讨论】:

files_grabbed = [glob.glob(e) for e in ['*.pdf', '*.cpp']] Novitoll 的解决方案很短,但最终会创建嵌套列表。 你总是可以这样做 ;) [f for f_ in [glob.glob(e) for e in ('*.jpg', '*.mp4')] for f in f_] files_grabbed = [glob.glob(e) for e in ['.pdf', '*.cpp']] 这会在文件列表中循环两次。在第一次迭代中检查 *.pdf,在第二次迭代中检查 *.cpp。有没有办法在一次迭代中完成它?每次检查组合条件?【参考方案2】:

glob 返回一个列表:为什么不直接运行多次并将结果连接起来?

from glob import glob
project_files = glob('*.txt') + glob('*.mdown') + glob('*.markdown')

【讨论】:

这可能是给出的最具可读性的解决方案。我会将ProjectFiles 的大小写更改为projectFiles,但是很好的解决方案。 请注意,在 python 3x 中Path.glob('*') 返回一个生成器,因此您需要在其周围放置一个list(...) 才能使用此技巧。 @MarcMaxmeister 不正确! glob 确实返回了一个生成器,但连接按预期工作,至少在 Python 3.5+ 中是这样。不过,我还没有一个快速的方法来测试早期的 Python。【参考方案3】:
from glob import glob

files = glob('*.gif')
files.extend(glob('*.png'))
files.extend(glob('*.jpg'))

print(files)

如果您需要指定路径,请循环匹配模式并将连接保留在循环内以简单起见:

from os.path import join
from glob import glob

files = []
for ext in ('*.gif', '*.png', '*.jpg'):
   files.extend(glob(join("path/to/dir", ext)))

print(files)

【讨论】:

【参考方案4】:

链接结果:

import itertools as it, glob

def multiple_file_types(*patterns):
    return it.chain.from_iterable(glob.iglob(pattern) for pattern in patterns)

然后:

for filename in multiple_file_types("*.txt", "*.sql", "*.log"):
    # do stuff

【讨论】:

glob.glob -> glob.iglob 以便迭代器链完全惰性求值 我找到了相同的解决方案,但不知道chain.from_iterable。所以这是相似的,但可读性较差:it.chain(*(glob.iglob(pattern) for pattern in patterns)).【参考方案5】:

这么多的答案表明 globbing 与扩展的数量一样多,我宁愿只 globbing 一次:

from pathlib import Path

files = (p.resolve() for p in Path(path).glob("**/*") if p.suffix in ".c", ".cc", ".cpp", ".hxx", ".h")

【讨论】:

使用一组扩展而不是列表来提高性能。 迄今为止最快的答案。您应该使用一组扩展,您可以更改为 Path(path).iterdir() do disallow recursive iteration。 @LouisLac 我使用纯基于集合的实现和纯基于列表的实现使用 8 个扩展和搜索数千个文件对此进行了测试。性能没有显着差异。 @MoutainX 集在扩展数显着增加(我认为有几千个)时确实开始优于列表。通常人们不会查找那么多扩展名,所以这在这里不会有什么不同,但这是一个很好的做法。 @LouisLac - 就整体速度而言,我的测试结果与***.com/a/56619011/463994 相似 - 最快的解决方案使用嵌套的 for 循环而不是 glob。喜欢for root, dirs, files in walk(path): for file in files: for ext in extensions:【参考方案6】:

例如,对于多个文件夹上的*.mp3*.flac,您可以这样做:

mask = r'music/*/*.[mf][pl][3a]*'
glob.glob(mask)

这个想法可以扩展到更多的文件扩展名,但是您必须检查这些组合是否与您在这些文件夹中可能拥有的任何其他不需要的文件扩展名匹配.所以,小心这个。

要将任意扩展列表自动组合成单个 glob 模式,您可以执行以下操作:

def multi_extension_glob_mask(mask_base, *extensions):
    mask_ext = ['[]'.format(''.join(set(c))) for c in zip(*extensions)]
    if not mask_ext or len(set(len(e) for e in extensions)) > 1:
        mask_ext.append('*')
    return mask_base + ''.join(mask_ext)

mask = multi_extension_glob_mask('music/*/*.', 'mp3', 'flac', 'wma')
print(mask)  # music/*/*.[mfw][pml][a3]*

【讨论】:

【参考方案7】:

使用 glob 是不可能的。您只能使用: * 匹配所有内容 ?匹配任何单个字符 [seq] 匹配 seq 中的任何字符 [!seq] 匹配任何不在 seq 中的字符

使用 os.listdir 和正则表达式来检查模式:

for x in os.listdir('.'):
  if re.match('.*\.txt|.*\.sql', x):
    print x

【讨论】:

用 $ 结束正则表达式以仅匹配文件名的结尾 我喜欢这种方法 - 如果 glob 的表现力不够强大,请升级到更强大的正则表达式系统,不要使用例如itertools 因为后续的模式更改也必须是 hacky(比如你想允许大写和小写)。哦,写'.*\.(txt|sql)' 可能会更干净 有什么理由更喜欢 os.listdir('.') 而不是 glob.iglob('.')?【参考方案8】:

虽然 Python 的默认 glob 并没有真正遵循 Bash 的 glob,但您可以使用其他库来执行此操作。我们可以在wcmatch's glob 中启用大括号。

>>> from wcmatch import glob
>>> glob.glob('*.md,ini', flags=glob.BRACE)
['LICENSE.md', 'README.md', 'tox.ini']

如果您愿意,您甚至可以使用extended glob patterns:

from wcmatch import glob
>>> glob.glob('*.@(md|ini)', flags=glob.EXTGLOB)
['LICENSE.md', 'README.md', 'tox.ini']

【讨论】:

这不带recursive标志 @Shamoon 不,它需要glob.GLOBSTAR 标志【参考方案9】:

与@BPL 的答案相同(计算效率高),但它可以处理任何 glob 模式而不是扩展:

import os
from fnmatch import fnmatch

folder = "path/to/folder/"
patterns = ("*.txt", "*.md", "*.markdown")

files = [f.path for f in os.scandir(folder) if any(fnmatch(f, p) for p in patterns)]

此解决方案既高效又方便。它还与glob 的行为非常匹配(参见documentation)。

请注意,使用内置包pathlib 会更简单:

from pathlib import Path

folder = Path("/path/to/folder")
patterns = ("*.txt", "*.md", "*.markdown")

files = [f for f in folder.iterdir() if any(f.match(p) for p in patterns)]

【讨论】:

很好的解决方案,谢谢! fnmatch(f, p) 实际上不应该是 fnmatch(f.name, p) - 因为 f 是一个 nt.DirEntry 对象,fnmatch 无法测试。 谢谢,实际上没有.name 也可以,所以我猜DirEntry 可以用fnmatch 测试。 有趣。我的抛出了一个错误。无论如何 - 谢谢! 我使用的是 Python 3.9,这可能在早期版本中已修复。 公平竞争。我使用它的系统仍在 3.5 上。 (是的,我知道...)【参考方案10】:

这是 Pat 答案的单行列表理解变体(其中还包括您想在特定项目目录中通配的内容):

import os, glob
exts = ['*.txt', '*.mdown', '*.markdown']
files = [f for ext in exts for f in glob.glob(os.path.join(project_dir, ext))]

您遍历扩展名 (for ext in exts),然后为每个扩展名获取与 glob 模式匹配的每个文件 (for f in glob.glob(os.path.join(project_dir, ext))。

此解决方案简短,并且没有任何不必要的 for 循环、嵌套列表理解或使代码混乱的函数。纯粹的、富有表现力的、pythonic Zen

此解决方案允许您拥有exts 的自定义列表,无需更新代码即可对其进行更改。 (这始终是一个好习惯!)

列表理解与 Laurent 的解决方案(我已投赞成票)中使用的相同。但我认为通常没有必要将单行分解为单独的函数,这就是我将其作为替代解决方案提供的原因。

奖励:

如果您不仅需要搜索单个目录,还需要搜索所有子目录,可以通过recursive=True 并使用多目录全局符号** 1

files = [f for ext in exts 
         for f in glob.glob(os.path.join(project_dir, '**', ext), recursive=True)]

这将为每个扩展调用glob.glob('<project_dir>/**/*.txt', recursive=True) 等等。

1 从技术上讲,** glob 符号只匹配一个或多个字符包括正斜杠 /(与单数 * glob 符号不同)。在实践中,您只需要记住,只要用正斜杠(路径分隔符)包围 **,它就会匹配零个或多个目录。

【讨论】:

【参考方案11】:

单线,只是为了它......

folder = "C:\\multi_pattern_glob_one_liner"
files = [item for sublist in [glob.glob(folder + ext) for ext in ["/*.txt", "/*.bat"]] for item in sublist]

输出:

['C:\\multi_pattern_glob_one_liner\\dummy_txt.txt', 'C:\\multi_pattern_glob_one_liner\\dummy_bat.bat']

【讨论】:

【参考方案12】:
files = glob.glob('*.txt')
files.extend(glob.glob('*.dat'))

【讨论】:

好的答案还提供了一些代码解释,甚至可能是您在代码背后的一些推理。【参考方案13】:

我已经发布了Formic,它以类似于 Apache Ant 的FileSet and Globs 的方式实现了多个包含。

可以实现搜索:

import formic
patterns = ["*.txt", "*.markdown", "*.mdown"]
fileset = formic.FileSet(directory=projectDir, include=patterns)
for file_name in fileset.qualified_files():
    # Do something with file_name

由于实现了完整的 Ant glob,您可以在每个模式中包含不同的目录,因此您可以只选择一个子目录中的那些 .txt 文件,而另一个子目录中的 .markdown 文件,例如:

patterns = [ "/unformatted/**/*.txt", "/formatted/**/*.mdown" ]

我希望这会有所帮助。

【讨论】:

【参考方案14】:

这是一个 Python 3.4+ pathlib 解决方案:

exts = ".pdf", ".doc", ".xls", ".csv", ".ppt"
filelist = (str(i) for i in map(pathlib.Path, os.listdir(src)) if i.suffix.lower() in exts and not i.stem.startswith("~"))

它还会忽略所有以~ 开头的文件名。

【讨论】:

【参考方案15】:

来这里寻求帮助后,我提出了自己的解决方案并想分享它。它基于 user2363986 的回答,但我认为这更具可扩展性。意思是,如果你有 1000 个扩展,代码看起来仍然有些优雅。

from glob import glob

directoryPath  = "C:\\temp\\*." 
fileExtensions = [ "jpg", "jpeg", "png", "bmp", "gif" ]
listOfFiles    = []

for extension in fileExtensions:
    listOfFiles.extend( glob( directoryPath + extension ))

for file in listOfFiles:
    print(file)   # Or do other stuff

【讨论】:

对我不起作用。我用directoryPath = "/Users/bla/bla/images_dir*." 我需要更多信息来为您调试...您是否遇到异常?此外,如果您使用的是 Windows,则该路径看起来不起作用(缺少驱动器号)。【参考方案16】:

根据我从经验测试中获得的结果,事实证明glob.glob 并不是通过扩展名过滤掉文件的更好方法。部分原因是:

通配符“语言”不允许完美的多重扩展规范。 前一点会导致根据文件扩展名获得不正确的结果。 经验证明,通配方法比大多数其他方法要慢。 即使很奇怪,其他文件系统对象也可以有“extensions”,文件夹也可以。

我已经测试(为了正确性和及时性)以下4 不同的方法来按扩展名过滤掉文件并将它们放在list 中:

from glob import glob, iglob
from re import compile, findall
from os import walk


def glob_with_storage(args):

    elements = ''.join([f'[i]' for i in args.extensions])
    globs = f'args.target/**/*elements'
    results = glob(globs, recursive=True)

    return results


def glob_with_iteration(args):

    elements = ''.join([f'[i]' for i in args.extensions])
    globs = f'args.target/**/*elements'
    results = [i for i in iglob(globs, recursive=True)]

    return results


def walk_with_suffixes(args):

    results = []
    for r, d, f in walk(args.target):
        for ff in f:
            for e in args.extensions:
                if ff.endswith(e):
                    results.append(path_join(r,ff))
                    break
    return results


def walk_with_regs(args):

    reg = compile('|'.join([f'i$' for i in args.extensions]))

    results = []
    for r, d, f in walk(args.target):
        for ff in f:
            if len(findall(reg,ff)):
                results.append(path_join(r, ff))

    return results

通过在我的笔记本电脑上运行上面的代码,我获得了以下自动解释的结果。

Elapsed time for '7 times glob_with_storage()':  0.365023 seconds.
mean   : 0.05214614
median : 0.051861
stdev  : 0.001492152
min    : 0.050864
max    : 0.054853

Elapsed time for '7 times glob_with_iteration()':  0.360037 seconds.
mean   : 0.05143386
median : 0.050864
stdev  : 0.0007847381
min    : 0.050864
max    : 0.052859

Elapsed time for '7 times walk_with_suffixes()':  0.26529 seconds.
mean   : 0.03789857
median : 0.037899
stdev  : 0.0005759071
min    : 0.036901
max    : 0.038896

Elapsed time for '7 times walk_with_regs()':  0.290223 seconds.
mean   : 0.04146043
median : 0.040891
stdev  : 0.0007846776
min    : 0.04089
max    : 0.042885

Results sizes:
0 2451
1 2451
2 2446
3 2446

Differences between glob() and walk():
0 E:\x\y\z\venv\lib\python3.7\site-packages\Cython\Includes\numpy
1 E:\x\y\z\venv\lib\python3.7\site-packages\Cython\Utility\CppSupport.cpp
2 E:\x\y\z\venv\lib\python3.7\site-packages\future\moves\xmlrpc
3 E:\x\y\z\venv\lib\python3.7\site-packages\Cython\Includes\libcpp
4 E:\x\y\z\venv\lib\python3.7\site-packages\future\backports\xmlrpc

Elapsed time for 'main':  1.317424 seconds.

通过扩展名过滤文件的最快方法,甚至是最丑陋的方法。也就是说,使用endswith() 方法嵌套for 循环和string 比较。

此外,如您所见,通配算法(使用模式 E:\x\y\z\**/*[py][pyc])即使只给出了 2 扩展名(pypyc)也会返回不正确的结果。

【讨论】:

walk 版本似乎在进行一些缓存,因为它们在第一次运行后变得更快。即便如此,在比较首次运行时,walk_with_suffixes 版本仍然是我测试中最快的。有没有办法清除缓存,这样重复测试就不会出现偏差?【参考方案17】:

Python 3

我们可以使用pathlib; .glob 仍然不支持通配多个参数或在大括号内(如在 POSIX shell 中),但我们可以轻松地 filter 结果。

例如,您可能最喜欢做的事情:

# NOT VALID
Path(config_dir).glob("*.ini,toml")
# NOR IS
Path(config_dir).glob("*.ini", "*.toml")

可以做:

filter(lambda p: p.suffix in ".ini", ".toml", Path(config_dir).glob("*"))

这还不算太糟。

【讨论】:

or 在 python glob 中也死了吗? /**/(*.txt|*.jpg) ?【参考方案18】:

不是glob,但这是使用列表理解的另一种方式:

extensions = 'txt mdown markdown'.split()
projectFiles = [f for f in os.listdir(projectDir) 
                  if os.path.splitext(f)[1][1:] in extensions]

【讨论】:

【参考方案19】:

以下函数_glob 为多个文件扩展名提供全局变量。

import glob
import os
def _glob(path, *exts):
    """Glob for multiple file extensions

    Parameters
    ----------
    path : str
        A file name without extension, or directory name
    exts : tuple
        File extensions to glob for

    Returns
    -------
    files : list
        list of files matching extensions in exts in path

    """
    path = os.path.join(path, "*") if os.path.isdir(path) else path + "*"
    return [f for files in [glob.glob(path + ext) for ext in exts] for f in files]

files = _glob(projectDir, ".txt", ".mdown", ".markdown")

【讨论】:

【参考方案20】:

您可以尝试制作一个手动列表,将现有的扩展名与您需要的扩展名进行比较。

ext_list = ['gif','jpg','jpeg','png'];
file_list = []
for file in glob.glob('*.*'):
  if file.rsplit('.',1)[1] in ext_list :
    file_list.append(file)

【讨论】:

【参考方案21】:
import os    
import glob
import operator
from functools import reduce

types = ('*.jpg', '*.png', '*.jpeg')
lazy_paths = (glob.glob(os.path.join('my_path', t)) for t in types)
paths = reduce(operator.add, lazy_paths, [])

https://docs.python.org/3.5/library/functools.html#functools.reduce https://docs.python.org/3.5/library/operator.html#operator.add

【讨论】:

【参考方案22】:

glob多种文件类型,需要循环调用多次glob()函数。由于此函数返回一个列表,因此您需要将列表连接起来。

例如,这个函数可以完成这项工作:

import glob
import os


def glob_filetypes(root_dir, *patterns):
    return [path
            for pattern in patterns
            for path in glob.glob(os.path.join(root_dir, pattern))]

简单用法:

project_dir = "path/to/project/dir"
for path in sorted(glob_filetypes(project_dir, '*.txt', '*.mdown', '*.markdown')):
    print(path)

您也可以使用glob.iglob() 来拥有一个迭代器:

返回一个迭代器,它产生与 glob() 相同的值,但实际上并没有同时存储它们。

def iglob_filetypes(root_dir, *patterns):
    return (path
            for pattern in patterns
            for path in glob.iglob(os.path.join(root_dir, pattern)))

【讨论】:

【参考方案23】:

一个 glob,许多扩展...但不完美的解决方案(可能与其他文件匹配)。

filetypes = ['tif', 'jpg']

filetypes = zip(*[list(ft) for ft in filetypes])
filetypes = ["".join(ch) for ch in filetypes]
filetypes = ["[%s]" % ch for ch in filetypes]
filetypes = "".join(filetypes) + "*"
print(filetypes)
# => [tj][ip][fg]*

glob.glob("/path/to/*.%s" % filetypes)

【讨论】:

【参考方案24】:

我遇到了同样的问题,这就是我想出的

import os, sys, re

#without glob

src_dir = '/mnt/mypics/'
src_pics = []
ext = re.compile('.*\.(||)$'.format('|'.join(['png', 'jpeg', 'jpg']).encode('utf-8')))
for root, dirnames, filenames in os.walk(src_dir):
  for filename in filter(lambda name:ext.search(name),filenames):
    src_pics.append(os.path.join(root, filename))

【讨论】:

【参考方案25】:

如果你使用pathlib,试试这个:

import pathlib

extensions = ['.py', '.txt']
root_dir = './test/'

files = filter(lambda p: p.suffix in extensions, pathlib.Path(root_dir).glob('**/*'))

print(list(files))

【讨论】:

【参考方案26】:

使用扩展列表并遍历

from os.path import join
from glob import glob

files = []
extensions = ['*.gif', '*.png', '*.jpg']
for ext in extensions:
   files.extend(glob(join("path/to/dir", ext)))

print(files)

【讨论】:

【参考方案27】:

上一个回答

glob('*.jpg') + glob('*.png')

这是一个较短的,

from glob import glob
extensions = ['jpg', 'png'] # to find these filename extensions

# Method 1: loop one by one and extend to the output list
output = []
[output.extend(glob(f'*.name')) for name in extensions]
print(output)

# Method 2: even shorter
# loop filename extension to glob() it and flatten it to a list
output = [p for p2 in [glob(f'*.name') for name in extensions] for p in p2]
print(output)

【讨论】:

对此代码示例添加解释将有助于改进此答案。【参考方案28】:

你可以使用过滤器:

import os
import glob

projectFiles = filter(
    lambda x: os.path.splitext(x)[1] in [".txt", ".mdown", ".markdown"]
    glob.glob(os.path.join(projectDir, "*"))
)

【讨论】:

【参考方案29】:

你也可以像这样使用reduce()

import glob
file_types = ['*.txt', '*.mdown', '*.markdown']
project_files = reduce(lambda list1, list2: list1 + list2, (glob.glob(t) for t in file_types))

这会为每个模式从glob.glob() 创建一个列表,并将它们简化为一个列表。

【讨论】:

【参考方案30】:

还有另一种解决方案(使用glob 使用多个匹配patterns 获取路径,并使用reduceadd 将所有路径组合到一个列表中):

import functools, glob, operator
paths = functools.reduce(operator.add, [glob.glob(pattern) for pattern in [
    "path1/*.ext1",
    "path2/*.ext2"]])

【讨论】:

以上是关于Python glob 多种文件类型的主要内容,如果未能解决你的问题,请参考以下文章

python 小技巧之获取固定下面包含的某种类型文件的个数

使用多个文件扩展名来查找文件

如何使用 os.walk 或 glob.glob 获取目录中所有类型的文件扩展名

Perl glob 在尝试匹配特定文件类型时返回误报

python 里面的glob模块是怎么用的

用 Python 定位特定类型文件