Python 是否可以将fnmatch.translate(“***.py”)转换为语义正确的正则表达式?
是否有任何简单的方法(或可能的方法)将Python 是否可以将fnmatch.translate(“***.py”)转换为语义正确的正则表达式?,python,regex,Python,Regex,是否有任何简单的方法(或可能的方法)将fnmatch.translate的输出转换为匹配零个或多个目录的正则表达式 >>> import fnmatch >>> fnmatch.translate("**/*.py") '.*.*\\/.*\\.py\\Z(?ms)' 我想要的是放在中的正则表达式片段 dirs = ['a.py', 'b/a.py', 'b/c/a.py', 'b/c/d/a.py'] r = fnmatch.translate("**/*
fnmatch.translate
的输出转换为匹配零个或多个目录的正则表达式
>>> import fnmatch
>>> fnmatch.translate("**/*.py")
'.*.*\\/.*\\.py\\Z(?ms)'
我想要的是放在中的正则表达式片段
dirs = ['a.py', 'b/a.py', 'b/c/a.py', 'b/c/d/a.py']
r = fnmatch.translate("**/*.py").replace(".*.*", "___")
所以
[d for d in dirs if re.match(r, d)] == dirs
此尝试在前两种情况下不匹配:
fnmatch.translate("**/*.py").replace('.*.*', "(.*/.*)*")
这与第一个不匹配:
fnmatch.translate("**/*.py").replace('.*.*', "(.*/?)*")
这让口译员很难受:
fnmatch.translate("**/*.py").replace('.*.*', "(.*|(.*/.*))*")
我很乐意得到一个解释为什么不可能的答案
更新:仅用空字符串替换***/
不起作用:
dirs = ['a.py', 'b/a.py', 'b/c/a.py', 'b/c/d/a.py']
def translate(pat, repl=None):
r = fnmatch.translate(pat)
if repl:
r = r.replace(".*.*", repl)
r = re.compile(r)
return [d for d in dirs if r.match(d)]
>>> print translate("**/*.py".replace("**/", ""))
['a.py', 'b/a.py', 'b/c/a.py', 'b/c/d/a.py'] # correct
>>> print translate("b/**/*.py".replace("**/", ""))
['b/a.py', 'b/c/a.py', 'b/c/d/a.py'] # correct
>>> print translate("b/**/b.py".replace("**/", ""))
['b/a.py'] # incorrect
实际上——除了一个*
之外,您根本不需要更改*
;相反,您需要将单个未配对的*
更改为[^/]*
因此,实际需要的转换表是——根本不使用fnmatch.translate()
,而是自己滚动:
`?` -> `.`
`.` -> `[.]`
`**/` -> `(?:.*/)?`
`*` -> `[^/]*`
正确处理[!foo]
及其类似的操作最好是从上游翻译代码复制逻辑
应用这些规则将***.py
转换为(?:.*/)?[^/]*[.]py
,这与问题中的所有四个名称匹配:
>>> import re
>>> dirs = ['a.py', 'b/a.py', 'b/c/a.py', 'b/c/d/a.py']
>>> py_re = re.compile('(?:.*/)?[^/]*[.]py')
>>> [ 'Matches' if py_re.match(x) else 'Non-Matching' for x in dirs ]
['Matches', 'Matches', 'Matches', 'Matches']
转换的一个实现:
def build_re(glob_str):
opts = re.compile('([.]|[*][*]/|[*]|[?])|(.)')
out = ''
for (pattern_match, literal_text) in opts.findall(glob_str):
if pattern_match == '.':
out += '[.]'
elif pattern_match == '**/':
out += '(?:.*/)?'
elif pattern_match == '*':
out += '[^/]*'
elif pattern_match == '?':
out += '.'
elif literal_text:
out += literal_text
return out
实际上——除了一个*
之外,您根本不需要更改*
;相反,您需要将单个未配对的*
更改为[^/]*
因此,实际需要的转换表是——根本不使用fnmatch.translate()
,而是自己滚动:
`?` -> `.`
`.` -> `[.]`
`**/` -> `(?:.*/)?`
`*` -> `[^/]*`
正确处理[!foo]
及其类似的操作最好是从上游翻译代码复制逻辑
应用这些规则将***.py
转换为(?:.*/)?[^/]*[.]py
,这与问题中的所有四个名称匹配:
>>> import re
>>> dirs = ['a.py', 'b/a.py', 'b/c/a.py', 'b/c/d/a.py']
>>> py_re = re.compile('(?:.*/)?[^/]*[.]py')
>>> [ 'Matches' if py_re.match(x) else 'Non-Matching' for x in dirs ]
['Matches', 'Matches', 'Matches', 'Matches']
转换的一个实现:
def build_re(glob_str):
opts = re.compile('([.]|[*][*]/|[*]|[?])|(.)')
out = ''
for (pattern_match, literal_text) in opts.findall(glob_str):
if pattern_match == '.':
out += '[.]'
elif pattern_match == '**/':
out += '(?:.*/)?'
elif pattern_match == '*':
out += '[^/]*'
elif pattern_match == '?':
out += '.'
elif literal_text:
out += literal_text
return out
这里是Charles翻译算法的非正则表达式实现
更新1:此版本处理前导的作为整个模式的否定,类似于node.js的minimatch()
更新2:以fnmatch
的方式处理否定有点复杂(基本上fnmatch
处理[!…]
类似于正则表达式[^…]
):
def翻译(pat):
r=“”
i=0
L=len(pat)
而我\[\!\]
j+=1
而j=L:
r+='\\['\\\未找到结束']',回溯
其他:
stuff=pat[i:j].替换('\\','\\\')
i=j+1
如果stuff[0]=='!':
stuff='^'+stuff[1:#翻译否定
elif stuff[0]='^':
stuff='\\'+stuff#引号^character
r='%s[%s]'%(r,stuff)
其他:
r+=重新逃逸(帕特[i])
#r+=pat[i]
i+=1
r+='\\Z(?ms)'
返回r
(否定码几乎是从fnmatch中一字不差地偷走的。)这里是Charles翻译算法的非正则表达式实现
更新1:此版本处理前导的作为整个模式的否定,类似于node.js的minimatch()
更新2:以fnmatch
的方式处理否定有点复杂(基本上fnmatch
处理[!…]
类似于正则表达式[^…]
):
def翻译(pat):
r=“”
i=0
L=len(pat)
而我\[\!\]
j+=1
而j=L:
r+='\\['\\\未找到结束']',回溯
其他:
stuff=pat[i:j].替换('\\','\\\')
i=j+1
如果stuff[0]=='!':
stuff='^'+stuff[1:#翻译否定
elif stuff[0]='^':
stuff='\\'+stuff#引号^character
r='%s[%s]'%(r,stuff)
其他:
r+=重新逃逸(帕特[i])
#r+=pat[i]
i+=1
r+='\\Z(?ms)'
返回r
(否定码几乎是从fnmatch.一字不差地偷走的)为什么不fnmatch.translate(“*.py”)
?因为这不允许例如“b/**/*.py”
,即b
下的所有.py文件(dirs
中的最后三项)。b/*.py
将具有相同的效果,由于*
存在隐式**
。它允许foo
下的所有.py
文件。尝试pattern=pattern.replace(“***/”,”)
。这对“b/***/a.py”
不起作用。为什么不fnmatch.translate(“*.py”)
?因为这不允许例如“b/***.py”
,即b下的所有.py文件