glob.glob中的正则表达式用法?

80
import glob

list = glob.glob(r'*abc*.txt') + glob.glob(r'*123*.txt') + glob.glob(r'*a1b*.txt')

for i in list:
  print i

这段代码可以列出当前文件夹中名称中包含'abc''123''a1b'的文件。

我该如何使用一个glob函数来完成这个功能?


我认为你做不到。Python的glob不支持“{}”,我认为那几乎是唯一的方法。 - mgilson
4个回答

112

最简单的方法是自己筛选glob结果。以下是使用简单的循环推导式进行筛选的示例:

import glob
res = [f for f in glob.glob("*.txt") if "abc" in f or "123" in f or "a1b" in f]
for f in res:
    print f

你也可以使用正则表达式而不是 glob

import os
import re
res = [f for f in os.listdir(path) if re.search(r'(abc|123|a1b).*\.txt$', f)]
for f in res:
    print f

(顺便提一下,将变量命名为list是一个不好的想法,因为list是Python中的一个类型...)


2
你是不是想说 或者 or 而不是 并且 and - Emmanuel
@Emmanuel 我不确定... OP提到了“文件名中同时包含'abc'、'123'和'a1b'”。但是看他的代码,我猜or实际上可能更合适。感谢您指出这一点。 - Schnouki
@user1561868 已更改 :) 不用担心英语,这也不是我的母语。 - Schnouki
8
res = [f for f in glob.glob(".txt") if re.match(r'[abc|123|a1b].', f)]这段代码的意思是筛选当前目录下所有以".txt"结尾的文件名,并且只保留文件名符合正则表达式"[abc|123|a1b]."的文件。其中,正则表达式中的方括号内表示匹配其中任意一个字符,竖线 "|" 表示或的关系,点号 "" 表示匹配任意数量的字符(包括零个)。 - Omar

41

我很惊讶这里没有使用筛选器的回答。

import os
import re

def glob_re(pattern, strings):
    return filter(re.compile(pattern).match, strings)

filenames = glob_re(r'.*(abc|123|a1b).*\.txt', os.listdir())
此函数接受任何返回字符串的迭代器,包括列表、元组、字典(如果所有键都是字符串)等。如果您想支持部分匹配,可以将 .match 更改为 .search。请注意,这显然返回一个生成器,因此如果您想在不迭代它们的情况下使用结果,可以自行将结果转换为列表,或者将返回语句包装在 list(...) 中。

1
回复:Evan 只有在我将 list() 添加到 filter() 函数中时,它才对我起作用。 def glob_re(pattern, strings): return list(filter(re.compile(pattern).match, strings)) 希望这能帮到你。 - argan
3
@argan Python中的各种操作都是惰性求值的,而filter就是其中之一。惰性求值在编写实际程序时非常有用,但当您在repl中进行交互式编程时,它会成为一个不断烦扰您的问题。我完全理解只需将其包装在列表中以进行交互式一次性编码,但当您处于非单行代码的领域时,最好利用惰性求值的优势。 - Evan
为了让你的程序工作,你只需要通过可迭代对象传递变量(就像你提到的list(x)一样)。例如:for path in glob_re(pattern, string): print(path) - Evan

17

这里有一个现成的方法可以做到这一点,基于其他答案。它不是最关键的性能问题,但它按描述工作;

def reglob(path, exp, invert=False):
    """glob.glob() style searching which uses regex

    :param exp: Regex expression for filename
    :param invert: Invert match to non matching files
    """

    m = re.compile(exp)

    if invert is False:
        res = [f for f in os.listdir(path) if m.search(f)]
    else:
        res = [f for f in os.listdir(path) if not m.search(f)]

    res = map(lambda x: "%s/%s" % ( path, x, ), res)
    return res

1
应该使用 match 来完全匹配名称。不要点踩。 - holdenweb
2
我更喜欢lambda x: os.path.join(path,x)这个写法。 - user_na
2
@user_na:更好的做法是应该在res列表推导式中完成,而不是第二次循环遍历所有匹配项。 - martineau
@martineau 的确。此外,if/else 可以直接在列表推导的 if 语句中完成。但是,使用 os.path.join 而不是字符串格式化可能是我要解决的第一件事情。 - user_na

1
for filename in glob.iglob(path_to_directory + "*.txt"):
    if filename.find("abc") != -1 or filename.find("123") != -1 or filename.find("a1b") != -1:
        print filename

3
你能在你的回答中加入一些解释吗? - kvorobiev
基本思路是迭代目录中文件名列表,并查找其中包含任何文本字符串的文件名。 - R.Camilo
如果你想要包含所有字符串名称的文件,只需将“or”更改为“and”。 - R.Camilo

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