我希望能够将 glob
格式的模式与字符串列表相匹配,而不是与文件系统中的实际文件相匹配。有什么方法可以做到这一点,或者将 glob
模式轻松转换为正则表达式?
原文由 Jason S 发布,翻译遵循 CC BY-SA 4.0 许可协议
我希望能够将 glob
格式的模式与字符串列表相匹配,而不是与文件系统中的实际文件相匹配。有什么方法可以做到这一点,或者将 glob
模式轻松转换为正则表达式?
原文由 Jason S 发布,翻译遵循 CC BY-SA 4.0 许可协议
glob
模块将 fnmatch
模块 用于 _各个路径元素_。
这意味着路径被分成目录名和文件名,如果目录名包含元字符(包含任何字符 [
, *
或 ?
) 然后 递归地 展开这些。
如果您有一个简单文件名的字符串列表,那么只需使用 fnmatch.filter()
函数 就足够了:
import fnmatch
matching = fnmatch.filter(filenames, pattern)
但如果它们包含完整路径,则您需要做更多的工作,因为生成的正则表达式不会考虑路径段(通配符不会排除分隔符,也不会针对跨平台路径匹配进行调整)。
您可以从路径构建一个简单的 trie ,然后将您的模式与它进行匹配:
import fnmatch
import glob
import os.path
from itertools import product
# Cross-Python dictionary views on the keys
if hasattr(dict, 'viewkeys'):
# Python 2
def _viewkeys(d):
return d.viewkeys()
else:
# Python 3
def _viewkeys(d):
return d.keys()
def _in_trie(trie, path):
"""Determine if path is completely in trie"""
current = trie
for elem in path:
try:
current = current[elem]
except KeyError:
return False
return None in current
def find_matching_paths(paths, pattern):
"""Produce a list of paths that match the pattern.
* paths is a list of strings representing filesystem paths
* pattern is a glob pattern as supported by the fnmatch module
"""
if os.altsep: # normalise
pattern = pattern.replace(os.altsep, os.sep)
pattern = pattern.split(os.sep)
# build a trie out of path elements; efficiently search on prefixes
path_trie = {}
for path in paths:
if os.altsep: # normalise
path = path.replace(os.altsep, os.sep)
_, path = os.path.splitdrive(path)
elems = path.split(os.sep)
current = path_trie
for elem in elems:
current = current.setdefault(elem, {})
current.setdefault(None, None) # sentinel
matching = []
current_level = [path_trie]
for subpattern in pattern:
if not glob.has_magic(subpattern):
# plain element, element must be in the trie or there are
# 0 matches
if not any(subpattern in d for d in current_level):
return []
matching.append([subpattern])
current_level = [d[subpattern] for d in current_level if subpattern in d]
else:
# match all next levels in the trie that match the pattern
matched_names = fnmatch.filter({k for d in current_level for k in d}, subpattern)
if not matched_names:
# nothing found
return []
matching.append(matched_names)
current_level = [d[n] for d in current_level for n in _viewkeys(d) & set(matched_names)]
return [os.sep.join(p) for p in product(*matching)
if _in_trie(path_trie, p)]
这个满口可以在路径的任何地方使用 glob 快速找到匹配项:
>>> paths = ['/foo/bar/baz', '/spam/eggs/baz', '/foo/bar/bar']
>>> find_matching_paths(paths, '/foo/bar/*')
['/foo/bar/baz', '/foo/bar/bar']
>>> find_matching_paths(paths, '/*/bar/b*')
['/foo/bar/baz', '/foo/bar/bar']
>>> find_matching_paths(paths, '/*/[be]*/b*')
['/foo/bar/baz', '/foo/bar/bar', '/spam/eggs/baz']
原文由 Martijn Pieters 发布,翻译遵循 CC BY-SA 3.0 许可协议
2 回答5.1k 阅读✓ 已解决
2 回答1.1k 阅读✓ 已解决
4 回答1.4k 阅读✓ 已解决
3 回答1.3k 阅读✓ 已解决
3 回答1.2k 阅读✓ 已解决
1 回答1.7k 阅读✓ 已解决
1 回答1.2k 阅读✓ 已解决
好的艺术家复制;伟大的艺术家 偷。
我偷了 ;)
fnmatch.translate
translates globs?
and*
to regex.
and.*
respectively.我调整它不。这一个
fnmatch.filter
,re.match
和re.search
工作。在此页面上找到的 glob 模式和字符串通过了测试。