Python glob多个文件类型

Rap*_*rex 114 python glob

有没有更好的方法在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') )
Run Code Online (Sandbox Code Playgroud)

use*_*312 128

也许有更好的方法,但如何:

>>> import glob
>>> types = ('*.pdf', '*.cpp') # the tuple of file types
>>> files_grabbed = []
>>> for files in types:
...     files_grabbed.extend(glob.glob(files))
... 
>>> files_grabbed   # the list of pdf and cpp files
Run Code Online (Sandbox Code Playgroud)

也许还有另一种方式,所以请等待其他人提出更好的答案.

  • `files_grabbed = [glob.glob(e)for e in ['*.pdf','*.cpp']]` (14认同)
  • Novitoll的解决方案很短,但最终会创建嵌套列表. (9认同)
  • 你总是可以这样做;)`[f为f_ in [glob.glob(e)for e in('*.jpg','*.mp4')] f f in f_] (7认同)
  • 这会在文件列表中循环两次。在第一次迭代中,它检查 *.pdf,在第二次迭代中,它检查 *.cpp。有没有办法在一次迭代中完成它?每次检查组合条件? (4认同)
  • 如果两个或多个扩展名与同一文件匹配,在上述任一解决方案中会如何发挥作用。在这种情况下,我们会有需要考虑的重复项...我认为该任务意味着我们需要每个唯一的文件,因此解决方案应该考虑到这一点。 (2认同)

use*_*986 40

from glob import glob

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

print(files)
Run Code Online (Sandbox Code Playgroud)

如果需要指定路径,请循环匹配模式并将连接保持在循环内以简化:

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)
Run Code Online (Sandbox Code Playgroud)


tzo*_*zot 35

结果结果:

import itertools as it, glob

def multiple_file_types(*patterns):
    return it.chain.from_iterable(glob.iglob(pattern) for pattern in patterns)
Run Code Online (Sandbox Code Playgroud)

然后:

for filename in multiple_file_types("*.txt", "*.sql", "*.log"):
    # do stuff
Run Code Online (Sandbox Code Playgroud)

  • glob.glob - > glob.iglob,以便迭代器链完全延迟评估 (12认同)
  • 我找到了相同的解决方案,但不知道“chain.from_iterable”。因此,这很相似,但可读性较差:`it.chain(*(glob.iglob(pattern) for pattern in paths))`。 (3认同)

BPL*_*BPL 35

如此多的答案表明 globbing 与扩展数量一样多,我更喜欢 globbing 一次:

from pathlib import Path

files = {p.resolve() for p in Path(path).glob("**/*") if p.suffix in [".c", ".cc", ".cpp", ".hxx", ".h"]}
Run Code Online (Sandbox Code Playgroud)

  • @LouisLac 我使用纯基于集合的实现和纯基于列表的实现进行了测试,使用 8 个扩展并搜索数千个文件。性能没有显着差异。 (10认同)
  • 使用一组扩展而不是列表来提高性能。 (2认同)

pat*_*ney 31

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

from glob import glob
ProjectFiles = glob('*.txt') + glob('*.mdown') + glob('*markdown')
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在 python 3x 中 `Path.glob('*')` 返回一个生成器,因此您需要在它周围放置一个 `list(...)` 才能使用此技巧。 (10认同)
  • @bers 是的,但那是因为 `Path.glob()` 与 `glob.glob()` 没有相同的语义。我的评论是关于 `glob.glob()`,它在 Python 3.10 中按原样工作:`glob.glob('*.md') + glob.glob('*.jpg')` 在 Python 3.10 中工作得很好。在 Python 3.7 中的工作方式相同:“Path.glob()”返回一个生成器,但“glob.glob()”返回一个列表。 (5认同)
  • 这可能是给出的最易读的解决方案。我会将 `ProjectFiles` 的大小写更改为 `projectFiles`,但是很好的解决方案。 (4认同)

Chr*_*ian 15

用glob是不可能的.你只能使用:
*匹配一切
?匹配任何单个字符
[seq]匹配seq
[!seq]中的任何字符匹配任何不在seq中的字符

使用os.listdir和regexp来检查模式:

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

  • 用$结束你的正则表达式只匹配文件名的结尾 (10认同)
  • 我喜欢这种方法 - 如果 glob 的表达能力不够强大,请升级到更强大的正则表达式系统,不要使用例如“itertools”来破解它,因为后续的模式更改也必须是 hacky 的(假设您想要允许上层和下层)小写)。哦,写“'.*\.(txt|sql)'”可能更干净 (2认同)

feq*_*wix 13

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

mask = r'music/*/*.[mf][pl][3a]*'
glob.glob(mask)
Run Code Online (Sandbox Code Playgroud)

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


fac*_*ser 11

虽然 Python 的默认 glob 并没有真正跟随 Bash 的 glob,但您可以使用其他库来做到这一点。我们可以在wcmatch 的 glob 中启用大括号。

>>> from wcmatch import glob
>>> glob.glob('*.{md,ini}', flags=glob.BRACE)
['LICENSE.md', 'README.md', 'tox.ini']
Run Code Online (Sandbox Code Playgroud)

如果这是您的偏好,您甚至可以使用扩展的 glob 模式

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


OJF*_*ord 9

蟒蛇3

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

例如,您可能理想地喜欢这样做:

# NOT VALID
Path(config_dir).glob("*.{ini,toml}")
# NOR IS
Path(config_dir).glob("*.ini", "*.toml")
Run Code Online (Sandbox Code Playgroud)

可以做:

filter(lambda p: p.suffix in {".ini", ".toml"}, Path(config_dir).glob("*"))
Run Code Online (Sandbox Code Playgroud)

这还不算更糟。


sch*_*ler 6

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

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

您遍历扩展名 ( 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)]
Run Code Online (Sandbox Code Playgroud)

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

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


Gil*_*Mor 5

单线,仅此而已..

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]
Run Code Online (Sandbox Code Playgroud)

输出:

['C:\\multi_pattern_glob_one_liner\\dummy_txt.txt', 'C:\\multi_pattern_glob_one_liner\\dummy_bat.bat']
Run Code Online (Sandbox Code Playgroud)


小智 5

files = glob.glob('*.txt')
files.extend(glob.glob('*.dat'))
Run Code Online (Sandbox Code Playgroud)

  • 好的答案还提供了一些代码解释,甚至可能提供了代码背后的一些推理。 (5认同)

Gio*_*ova 5

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

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

我已经测试了(为了及时的正确性和效率)以下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
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

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

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


Lou*_*Lac 5

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

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)]
Run Code Online (Sandbox Code Playgroud)

这种解决方案既高效又方便。它还与glob(请参阅文档)的行为密切匹配。

请注意,使用内置包更简单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)]
Run Code Online (Sandbox Code Playgroud)