我想翻转一个正则表达式。即,给定一个正则表达式,我想生成任何匹配该正则表达式的字符串。
我知道如何从理论计算机科学背景下使用有限状态机来完成此操作,但我只想知道是否有人已经编写了可执行此操作的库。:)
我正在使用Python,所以我希望有一个Python库。
再次强调,我只想要一个可以匹配正则表达式的字符串。像"." 或 ".*" 这样的东西会使无数个字符串满足正则表达式,但是我不关心所有选项。
我愿意这个库只在某些正则表达式子集上工作。
我想翻转一个正则表达式。即,给定一个正则表达式,我想生成任何匹配该正则表达式的字符串。
我知道如何从理论计算机科学背景下使用有限状态机来完成此操作,但我只想知道是否有人已经编写了可执行此操作的库。:)
我正在使用Python,所以我希望有一个Python库。
再次强调,我只想要一个可以匹配正则表达式的字符串。像"." 或 ".*" 这样的东西会使无数个字符串满足正则表达式,但是我不关心所有选项。
我愿意这个库只在某些正则表达式子集上工作。
虽然我觉得这没有太多意义,但还是来试试:
import re
import string
def traverse(tree):
retval = ''
for node in tree:
if node[0] == 'any':
retval += 'x'
elif node[0] == 'at':
pass
elif node[0] in ['min_repeat', 'max_repeat']:
retval += traverse(node[1][2]) * node[1][0]
elif node[0] == 'in':
if node[1][0][0] == 'negate':
letters = list(string.ascii_letters)
for part in node[1][1:]:
if part[0] == 'literal':
letters.remove(chr(part[1]))
else:
for letter in range(part[1][0], part[1][1]+1):
letters.remove(chr(letter))
retval += letters[0]
else:
if node[1][0][0] == 'range':
retval += chr(node[1][0][1][0])
else:
retval += chr(node[1][0][1])
elif node[0] == 'not_literal':
if node[1] == 120:
retval += 'y'
else:
retval += 'x'
elif node[0] == 'branch':
retval += traverse(node[1][1][0])
elif node[0] == 'subpattern':
retval += traverse(node[1][1])
elif node[0] == 'literal':
retval += chr(node[1])
return retval
print traverse(re.sre_parse.parse(regex).data)
我从正则表达式语法一直到分组的所有内容都进行了整理-- 这似乎是一个合理的子集 -- 我忽略了一些细节,比如行尾。 错误处理等等留给读者自己来实现。
在正则表达式中的12个特殊字符中,我们完全可以忽略其中的6个(其中两个甚至连它们应用的原子都可以忽略),有4.5个会导致简单替换,另外1.5个需要我们认真思考。
我认为,这样做出来的结果并不是太有趣。
r'(?=[a-z])(?=[x-z])'
- Vegard(?=[a-z])
替换为a
。完成了,毫无意思。 - user3850(?=...)
,你现在需要同时找到与两个或更多的正则表达式匹配的内容。对于我给出的示例,您只能选择范围 [x-z]
中的字母,因为那是重叠范围。所以这不再是那么直接了。 - Vegard我不知道是否有任何模块能够完成这个任务。如果在Cookbook或PyPI中没有找到类似的内容,你可以尝试自己编写,使用(re.sre_parse)未公开文档的模块。这可能会帮助你入门:
In [1]: import re
In [2]: a = re.sre_parse.parse("[abc]+[def]*\d?z")
In [3]: a
Out[3]: [('max_repeat', (1, 65535, [('in', [('literal', 97), ('literal', 98), ('literal', 99)])])), ('max_repeat', (0, 65535, [('in', [('literal', 100), ('literal', 101), ('literal', 102)])])), ('max_repeat', (0, 1, [('in', [('category', 'category_digit')])])), ('literal', 122)]
In [4]: eval(str(a))
Out[4]:
[('max_repeat',
(1, 65535, [('in', [('literal', 97), ('literal', 98), ('literal', 99)])])),
('max_repeat',
(0,
65535,
[('in', [('literal', 100), ('literal', 101), ('literal', 102)])])),
('max_repeat', (0, 1, [('in', [('category', 'category_digit')])])),
('literal', 122)]
In [5]: a.dump()
max_repeat 1 65535
in
literal 97
literal 98
literal 99
max_repeat 0 65535
in
literal 100
literal 101
literal 102
max_repeat 0 1
in
category category_digit
literal 122
Exrex 可以从正则表达式生成字符串。
Exrex 是一个命令行工具和 Python 模块,可根据给定的正则表达式生成所有匹配的字符串或随机匹配的字符串等。
示例:
>>> exrex.getone('\d{4}-\d{4}-\d{4}-[0-9]{4}')
'3096-7886-2834-5671'
虽然其他答案使用了re引擎来解析元素,但我自己编写了一个解析re并返回最小匹配模式的代码(请注意,它无法处理 [^ads]、复杂的分组结构以及行首/行尾特殊字符)。如果您需要的话,我可以提供单元测试 :)
import re
class REParser(object):
"""Parses an RE an gives the least greedy value that would match it"""
def parse(self, parseInput):
re.compile(parseInput) #try to parse to see if it is a valid RE
retval = ""
stack = list(parseInput)
lastelement = ""
while stack:
element = stack.pop(0) #Read from front
if element == "\\":
element = stack.pop(0)
element = element.replace("d", "0").replace("D", "a").replace("w", "a").replace("W", " ")
elif element in ["?", "*"]:
lastelement = ""
element = ""
elif element == ".":
element = "a"
elif element == "+":
element = ""
elif element == "{":
arg = self._consumeTo(stack, "}")
arg = arg[:-1] #dump the }
arg = arg.split(",")[0] #dump the possible ,
lastelement = lastelement * int(arg)
element = ""
elif element == "[":
element = self._consumeTo(stack, "]")[0] # just use the first char in set
if element == "]": #this is the odd case of []<something>]
self._consumeTo(stack, "]") # throw rest away and use ] as first element
elif element == "|":
break # you get to an | an you have all you need to match
elif element == "(":
arg = self._consumeTo(stack, ")")
element = self.parse( arg[:-1] )
retval += lastelement
lastelement = element
retval += lastelement #Complete the string with the last char
return retval
def _consumeTo(self, stackToConsume, endElement ):
retval = ""
while not retval.endswith(endElement):
retval += stackToConsume.pop(0)
return retval
(foo|bar)(baz|quux)
可以扩展为列表 ['foobaz', 'fooquux', 'barbaz', 'barquux']
。hypothesis
包有一个名为 from_regex
的策略,可以实现这一点:
>>> from hypothesis import strategies as st
>>> regex_strategy = st.from_regex(r'[0-9]{4}-[0-9]{8}-[0-9]{16}', fullmatch=True)
>>> regex_strategy.example()
'5000-00000000-0000000000000000'
>>> regex_strategy.example()
'8934-72779254-0542308893797380'
>>> regex_strategy.example()
'0000-00000000-0000000000000000'
>>> regex_strategy.example()
'9000-00000000-0000000000000000'
>>> regex_strategy.example()
'2791-03881838-5840296980736661'
请注意,hypothesis
是一个用于模糊测试而不仅仅是一个简单数据生成器的库。因此需要谨慎:如果您例如修改上面示例中的模式为\d{4}-\d{8}-\d{16}
,则生成的示例可能会像'۵෫൮۹-๕꯱౦໓᠘௮᭒৮-꯳೩꘥៦६༣߃੮8༡႑۹᪒٩꧶'
这样,看起来可能出乎意料,但与模式匹配。
Regexp::Genex
。从模块描述来看,它似乎依赖于Perl正则表达式引擎的内部细节,因此即使从理论上讲也可能没有用处(我没有调查实现,只是根据文档中的注释)。