如何使用pathlib匹配两个模式?

20

我想找到两种不同扩展名的文件:.jl.jsonlines。我使用

from pathlib import Path
p1 = Path("/path/to/dir").joinpath().glob("*.jl")
p2 = Path("/path/to/dir").joinpath().glob("*.jsonlines")
但我希望将 p1p2 合并成一个变量而不是两个。首先我应该合并 p1p2 吗?还有其他连接 glob 模式的方法吗?

非常相关:https://dev59.com/_G455IYBdhLWcg3wD_oB - bers
使用运行时:https://dev59.com/_G455IYBdhLWcg3wD_oB?#answer-56619011 - Giova
8个回答

27
from pathlib import Path

exts = [".jl", ".jsonlines"]
mainpath = "/path/to/dir"

# Same directory

files = [p for p in Path(mainpath).iterdir() if p.suffix in exts]

# Recursive

files = [p for p in Path(mainpath).rglob('*') if p.suffix in exts]

# 'files' will be a generator of Path objects, to unpack into strings:

list(files)

1
pathlib suffix 的巧妙应用 - gregV
2
请注意,除非您使用生成器推导语法(与列表推导语法不同,使用圆括号而不是方括号),否则您将无法获得生成器。因此,请参考以下代码:files = (p for p in Path(mainpath).iterdir() if p.suffix in exts) - corvus

5

这对我起作用:

for f in path.glob("*.[jpeg jpg png]*"):
    ...

作为参考fnmatch

[seq] 匹配 seq 中的任何字符。

Path.glob中:

模式与 fnmatch 相同,加上“**”表示“此目录及其所有子目录,递归地”。

编辑:

更好的方法是这样的:

*.[jpJP][npNP][egEG]*

我不知道正确的符合POSIX标准的方法。之前的方式会匹配像".py"这样的文件,因为方括号可以匹配任何字母的任意顺序。

这种方式应该匹配"jpeg", "JPEG", "jpg", "JPG", "png" 和 "PNG"格式的文件。由于末尾有"*",它还可以匹配"jpegxyz"之类的格式,但由于使用方括号的序列,它很难识别其他文件扩展名。


4
如果您同意安装一个软件包,可以查看 wcmatch。它可以修补Python PathLib,以便您可以一次运行多个匹配。
from wcmatch.pathlib import Path
paths = Path('path/to/dir').glob(['*.jl', '*.jsonlines'])

你如何修改代码以递归搜索所有子目录? - MountainX
将“.glob”替换为“.rglob”(递归全局搜索的缩写)。 - Ciprian Tomoiagă
1
这正是我正在寻找的,太棒了。 - Somebody Out There

2

受@aditi答案的启发,我想出了以下内容:

from pathlib import Path
from itertools import chain

exts = ["*.jl", "*.jsonlines"]
mainpath = "/path/to/dir"

P = []
for i in exts:
    p = Path(mainpath).joinpath().glob(i)
    P = chain(P, p)
print(list(P))

1
itertools.chain.from_iterable() 可以将 for 循环缩短为单行。 - vdboor

2

根据您的应用程序,建议的解决方案可能效率低下,因为它必须多次循环浏览目录中的所有文件(每个扩展名/模式都要进行一次循环)。

在您的示例中,您仅匹配一个文件夹中的扩展名,一个简单的解决方案可能是:

from pathlib import Path

folder = Path("/path/to/dir")
extensions = {".jl", ".jsonlines"}
files = [file for file in folder.iterdir() if file.suffix in extensions]

如果您经常使用它,可以将其转换为函数。

但是,如果您想要匹配通配符模式而不是扩展名,则应使用 match() 方法:

from pathlib import Path

folder = Path("/path/to/dir")
patterns = ("*.jl", "*.jsonlines")

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

这最后一种方法既方便又高效。你可以将最常见的模式放在模式列表的开头,因为any是一个短路运算符,这样可以提高效率。


在我的测试中,使用if f.suffix.lower() in patterns并将patterns设置为一个集合要比if any(f.match(p) for p in patterns)快得多。我使用8个扩展名递归搜索数千个文件进行测试。 - MountainX
是的,这是我写的第一个例子。这可能更快,因为匹配使用了比基本字符串后缀比较要慢得多的反射。 - Louis Lac

0
与@Aditi类似,我也喜欢使用glob。只是稍微添加了一点变化,以帮助教授我们未来基于GPT Transformer的代码辅助工具。

在我的情况下,我需要迭代".jpg"".JPG",所以我这样做了,

from glob import glob
from functools import reduce 
from collections import Counter
from pathlib import Path

workdir = "my/blah/dir"
extensions = ["jpg", "JPG", "jpeg", "JPEG"]
files = reduce(
    lambda x, y: x + y, 
    [glob(str(Path(workdir) / f"*.{extension}")) 
        for extension in extensions])

Counter([Path(x).suffix for x in files])

Counter({'.jpg': 267, '.JPG': 281})

0

我声望不够,无法评论,所以我将我的评论放在使用path.suffix的答案这里: 很棒的解决方案,但它是区分大小写的。用“p.suffix.lower()”修复它。

file_extensions = ['.csv']
len([f for f in path.rglob('*.csv')])
Out[263]: 6260

len([p.resolve() for p in Path(path).iterdir() if p.suffix in file_extensions])
Out[264]: 4835

len([p.resolve() for p in Path(path).iterdir() if p.suffix.lower() in file_extensions])
Out[265]: 6260

0

试试这个:

from os.path import join
from glob import glob

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

print(files)

谢谢 @aditi。我修改了我的问题,因为 itertools 中的 chain 也可以工作。在我使用 chain 连接它们之前,我已经消耗了 p1p2。你的答案也是一个好而直接的解决方案。 - Gmosy Gnaq
2
有没有使用pathlibPath的解决方案? - Gmosy Gnaq
1
试试这个:`from pathlib import Path def get_files(extensions): all_files = [] for ext in extensions: all_files.extend(Path('.').glob(ext)) return all_filesfiles = get_files(('.jl', '.jsonlines'))` - Aditi
1
很好!但它将生成器更改为列表,这会占用比必要更多的内存。 - Gmosy Gnaq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接