Invertir una expresión regular en Python

Quiero revertir una expresión regular. Dada una expresión regular, quiero producir cualquier cadena que coincida con esa expresión regular.

Sé cómo hacer esto desde un fondo teórico de computación usando una máquina de estados finitos, pero solo quiero saber si alguien ya ha escrito una biblioteca para hacer esto. 🙂

Estoy usando Python, así que me gustaría una biblioteca de Python.

Para reiterar, solo quiero una cadena que coincida con la expresión regular. Cosas como “.” o “. *” haría que una cantidad infinita de cadenas coincidiera con la expresión regular, pero no me importan todas las opciones.

Estoy dispuesto a que esta biblioteca solo funcione en un cierto subconjunto de expresiones regulares.

Alguien más tenía una pregunta similar (¿duplicada?) Aquí , y me gustaría ofrecer una pequeña biblioteca de ayuda para generar cadenas aleatorias con Python en las que he estado trabajando.

Incluye un método, xeger() que te permite crear una cadena desde una expresión regular:

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

En este momento, funciona con la mayoría de las expresiones regulares básicas, pero estoy seguro de que podría mejorarse.

Aunque no veo mucho sentido en esto, aquí va:

 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) 

Tomé todo, desde la syntax de expresiones regulares hasta los grupos, esto parece un subconjunto razonable, e ignoré algunos detalles, como los finales de línea. El manejo de errores, etc. se deja como un ejercicio para el lector.

De los 12 caracteres especiales en una expresión regular, podemos ignorar 6 completamente (2 incluso con el átomo al que se aplican), 4.5 llevan a un reemplazo trivial y 1.5 nos hacen pensar.

Lo que sale de esto no es demasiado terriblemente interesante, creo.

No sé de ningún módulo para hacer esto. Si no encuentra nada como esto en el Cookbook o PyPI, puede intentar rodar el suyo propio, usando el módulo re.sre_parse (no documentado). Esto podría ayudarte a comenzar:

 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 

A menos que su expresión regular sea extremadamente simple (es decir, sin estrellas o ventajas), habrá infinitas cadenas que la emparejan. Si su expresión regular solo involucra concatenación y alternancia, entonces puede expandir cada alternancia a todas sus posibilidades, por ejemplo (foo|bar)(baz|quux) puede expandirse a la lista ['foobaz', 'fooquux', 'barbaz', 'barquux'] .

Mientras que las otras respuestas utilizan el motor de referencia para analizar los elementos que he creado, los analiza y devuelve un patrón mínimo que coincidiría. (Tenga en cuenta que no maneja [^ anuncios], construcciones de agrupación sofisticadas, caracteres especiales de inicio / final de línea). Puedo proporcionar las pruebas de la unidad si realmente te gusta 🙂

 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 []] 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 

Echa un vistazo a el inversor regex en UtilityMill . (El código fuente es visible, basado en este ejemplo de la wiki de pyparsing.)

No he visto un módulo de Python para hacer esto, pero sí vi una implementación (parcial) en Perl: Regexp::Genex . A partir de la descripción del módulo, parece que la implementación se basa en los detalles internos del motor de expresiones regulares de Perl, por lo que puede no ser útil incluso desde un punto de vista teórico (no he investigado la implementación, solo voy por los comentarios en la documentación). ).

Creo que hacer lo que propone en general es un problema difícil y puede requerir el uso de técnicas de progtwigción no deterministas. Un comienzo sería analizar la expresión regular y construir un árbol de análisis, luego atravesar el árbol y generar cadenas de muestra a medida que avanza. Los bits desafiantes probablemente serán cosas como referencias inversas y evitar bucles infinitos en su implementación.

Exrex puede crear cadenas de expresiones regulares.

Exrex es una herramienta de línea de comandos y un módulo de Python que genera cadenas que coinciden todas o aleatoriamente con una expresión regular dada y más.

Ejemplo:

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