在Python中反转正则表达式

时间:2009-01-29 18:05:22

标签: python regex

我想要反转正则表达式。即给定正则表达式,我想生成任何字符串,以匹配该正则表达式。

我知道如何使用有限状态机从理论计算机科学背景中做到这一点,但我只是想知道是否有人已经编写了一个库来执行此操作。 :)

我正在使用Python,所以我想要一个Python库。

重申一下,我只想要一个字符串来匹配正则表达式。像 ”。”或者“。*”会使无限量的字符串与正则表达式匹配,但我并不关心所有选项。

我愿意这个库只能用于正则表达式的某个子集。

8 个答案:

答案 0 :(得分:21)

其他人有类似(重复?)的问题here,我想为generating random strings with Python提供一个我一直在努力的小帮手库。

它包含一个允许您从正则表达式创建字符串的方法xeger()

>>> import rstr
>>> rstr.xeger(r'[A-Z]\d[A-Z] \d[A-Z]\d')
u'M5R 2W4'

现在,它适用于大多数基本正则表达式,但我确信它可以改进。

答案 1 :(得分:16)

虽然我在这方面没有多大意义,但这里有:

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)

我从Regular Expression Syntax到群组中取得了所有内容 - 这似乎是一个合理的子集 - 我忽略了一些细节,比如行结尾。错误处理等留给读者练习。

在正则表达式中的12个特殊字符中,我们可以完全忽略6个(即使它们应用的原子也是2个),4.5导致一个简单的替换,1.5让我们实际思考。

我认为,这一点并不是非常有趣。

答案 2 :(得分:11)

我不知道有任何模块可以做到这一点。如果你在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

答案 3 :(得分:5)

除非你的正则表达式非常简单(即没有星星或加号),否则会有无限多的字符串与之匹配。如果你的正则表达式只涉及连接和交替,那么你可以将每个交替扩展到它的所有可能性,例如, (foo|bar)(baz|quux)可以扩展到列表['foobaz', 'fooquux', 'barbaz', 'barquux']

答案 4 :(得分:4)

虽然其他答案使用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

答案 5 :(得分:4)

查看regex inverter at UtilityMill。 (源代码是可见的,基于来自pyparsing wiki的this example。)

答案 6 :(得分:2)

我没有看过Python模块,但我确实在Perl中看到了(部分)实现:Regexp::Genex。从模块描述来看,听起来实现依赖于Perl正则表达式引擎的内部细节,所以即使从理论的角度来看它也没有用处(我没有调查过实现,只是通过文档中的注释) )。

我认为做一般的建议是一个难题,可能需要使用非确定性编程技术。一个开始是解析正则表达式并构建一个解析树,然后遍历树并构建样本字符串。具有挑战性的位可能就像反向引用和避免实现中的无限循环一样。

答案 7 :(得分:1)

Exrex可以从正则表达式创建字符串。

  

Exrex是一个命令行工具和python模块,可以为给定的正则表达式生成所有或随机匹配的字符串等。

示例:

>>> exrex.getone('\d{4}-\d{4}-\d{4}-[0-9]{4}')
'3096-7886-2834-5671'