在Python中反转正则表达式

问题描述 投票:0回答:9

我想反转正则表达式。 IE。给定一个正则表达式,我想生成与该正则表达式匹配的any字符串。

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

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

重申一下,我只想要 one 与正则表达式匹配的字符串。像“.”之类的东西或“.*”将使无限数量的字符串与正则表达式匹配,但我不关心所有选项。

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

python regex
9个回答
29
投票

其他人也有类似的(重复?)问题here,我想提供一个小帮助库,用于我一直在研究的使用Python生成随机字符串

它包含一个方法,

xeger()

,允许您从正则表达式创建字符串:

>>> import rstr >>> rstr.xeger(r'[A-Z]\d[A-Z] \d[A-Z]\d') u'M5R 2W4'
目前,它适用于最基本的正则表达式,但我确信它可以改进。


19
投票
虽然我认为这没有多大意义,但这里是:

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 个(其中 2 个甚至可以忽略它们所适用的原子),4.5 个导致简单的替换,1.5 个让我们真正思考。

我认为,由此产生的结果并不是太有趣。


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

8
投票

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

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

示例:

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

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

4
投票
除非您的正则表达式非常简单(即没有星号或加号),否则将会有无限多个与其匹配的字符串。 如果您的正则表达式仅涉及串联和交替,那么您可以将每个交替扩展到其所有可能性,例如

(foo|bar)(baz|quux)

可以扩展到列表
['foobaz', 'fooquux', 'barbaz', 'barquux']


3
投票

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༡႑۹᪒٩꧶'
,这可能看起来出乎意料,但与模式匹配。


2
投票
我还没有看到 Python 模块可以做到这一点,但我确实看到了 Perl 中的(部分)实现:

Regexp::Genex

。从模块描述来看,听起来实现依赖于Perl正则表达式引擎的内部细节,因此即使从理论角度来看它也可能没有用(我没有研究实现,只是通过文档中的注释).

我认为一般来说做你建议的事情是一个难题,可能需要使用非确定性编程技术。首先是解析正则表达式并构建解析树,然后遍历树并构建示例字符串。具有挑战性的部分可能是诸如反向引用和避免实现中的无限循环之类的事情。


0
投票
虽然这个问题已经很老了,但我最近开发了一个名为

regex-enumerator 的 Python 库,它可以枚举与有限正则表达式匹配的所有字符串。如果您愿意使用 Python 来完成您的任务,这个库可能会有用。它在 PyPI 上可用,因此您可以使用以下命令轻松安装它:

pip install regex-enumerator
以下是如何使用它的示例:

from regex_enumerator import RegexEnumerator # Initialize the RegexEnumerator with your regex pattern re = RegexEnumerator(r'a[0-9]b') # Generate strings that match the pattern, one at a time print(re.next()) # a0b print(re.next()) # a1b print(re.next()) # a2b
该库专为您需要系统地枚举正则表达式的所有可能匹配的情况而设计,使其适合诸如详尽测试或生成数据集之类的任务。

虽然最初的问题专门要求 Java 解决方案,但如果您愿意利用 Python,则该库可以作为替代方法。

© www.soinside.com 2019 - 2024. All rights reserved.