在Python中颠倒正则expression式
我想扭转一个正则expression式。 即给定一个正则expression式,我想产生任何string,将匹配该正则expression式。
我知道如何从理论计算机科学背景使用有限状态机来做到这一点,但我只想知道是否有人已经写了一个库来做到这一点。 🙂
我正在使用Python,所以我想要一个Python库。
重申一下,我只想要一个匹配正则expression式的string。 像 ”。” 或“。*”将使无限量的string匹配正则expression式,但我不关心所有选项。
我愿意这个库只能用于某个正则expression式子集。
有人在这里有一个类似的(重复?)的问题,我想提供一个帮助程序库,用于生成随机Pythonstring ,我一直在努力。
它包括一个方法, xeger()
,允许您从正则expression式创build一个string:
>>> import rstr >>> rstr.xeger(r'[AZ]\d[AZ] \d[AZ]\d') u'M5R 2W4'
现在,它适用于大多数基本的正则expression式,但我相信它可以改进。
虽然我在这里看不出多less意义,但是这里是:
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)
我将正则expression式语法中的所有内容都放到组中 – 这看起来像是一个合理的子集 – 而我忽略了一些细节,比如行尾。 error handling等作为练习留给读者。
在正则expression式中的12个特殊字符中,我们完全可以忽略6个(即使是应用了它们的primefaces),4.5导致了一个微不足道的replace,1.5导致我们真正的思考。
我认为,从中得出的结论并不太有趣。
我不知道有任何模块来做到这一点。 如果在Cookbook或PyPI中没有find类似的东西,那么可以尝试使用(未logging的)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
除非你的正则expression式非常简单(即没有星号或加号),否则会有无数的string匹配它。 如果你的正则expression式只涉及到连接和交替,那么你可以扩展每个变化到所有的可能性,例如(foo|bar)(baz|quux)
可以扩展到列表['foobaz', 'fooquux', 'barbaz', 'barquux']
。
而其他答案使用重新引擎来parsing出我自己parsing的元素,并返回一个匹配的最小模式。 (注意它不处理[^广告],花式分组结构,行开始/结束特殊字符)。 我可以提供unit testing,如果你真的喜欢:)
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
查看UtilityMill的正则expression式逆变器 。 (根据pyparsing wiki中的示例 ,可以查看源代码。)
我还没有看到一个Python模块来做到这一点,但我在Perl中看到了一个(部分)实现: Regexp::Genex
。 从模块描述来看,这听起来像实现依赖于Perl的正则expression式引擎的内部细节,所以即使从理论的angular度来看也可能没有用处(我没有调查过实现,只是通过文档中的注释)。
我认为,一般来说做你提出的是一个难题,可能需要使用非确定性编程技术。 一开始就是parsing正则expression式并构build一个parsing树,然后遍历树并随时创build示例string。 具有挑战性的部分可能是反向引用,避免了实现中的无限循环。
我最近在C ++中完成了自动正则expression式逆转: http : //www.benhanson.net/lexertl/blog.html#rev_regex1
要获得Python版本,编写代码生成器来输出lexertl的Python代码将非常简单。 我不是Python的,但如果你想帮助语法,我很高兴创build一个文件来做到这一点。
Exrex可以从正则expression式创buildstring。
Exrex是一个命令行工具和python模块,可以为给定的正则expression式生成全部或者随机匹配的string。
例:
>>> exrex.getone('\d{4}-\d{4}-\d{4}-[0-9]{4}') '3096-7886-2834-5671'