49 votes

Inverser une expression régulière en python

Je veux inverser une expression régulière. c'est à dire étant donné une expression régulière, je veux produire toute chaîne de caractères qui correspond à celui de la regex.

Je sais comment le faire à partir d'une théorie de l'informatique d'arrière-plan à l'aide de la machine à état fini, je veux juste savoir si quelqu'un a déjà écrit une bibliothèque pour ce faire. :)

Je suis à l'aide de Python, donc je voudrais une bibliothèque python.

Pour réitérer, je veux seulement une chaîne de caractères qui correspond à l'expression régulière. Des choses comme "." ou ".*" ferait une quantité infinie de chaînes correspondent à l'expression régulière, mais je ne m'inquiète pas à propos de toutes les options.

Je suis prêt pour cette bibliothèque ne fonctionne que sur un certain sous-ensemble de regex.

28voto

bjmc Points 350

Quelqu'un d'autre a eu un similaire (double?) question ici, et j'aimerais apporter une petite aide de la bibliothèque pour la génération aléatoire des chaînes de caractères avec Python que j'ai travaillé.

Il inclut une méthode, xeger() qui vous permet de créer une chaîne de caractères à partir d'une expression régulière:

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

Maintenant, il fonctionne avec la plupart de base des expressions régulières, mais je suis sûr qu'il pourrait être amélioré.

19voto

hop Points 15423

Bien que je ne vois pas beaucoup de sens en ce, va ici:

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)

J'ai tout pris de la Syntaxe d'Expression Régulière à des groupes -- cela semble raisonnable sous-ensemble -- et je l'ai ignoré certains détails, comme les fins de ligne. Erreur de manipulation, etc. est laissé comme exercice au lecteur.

Du 12 caractères spéciaux dans une expression régulière, on peut les ignorer, 6 complètement (2 même avec l'atome elles s'appliquent), 4.5 conduire à un trivial de remplacement et 1.5 nous font réellement penser.

Ce qui vient n'est pas trop terriblement intéressant, je pense.

13voto

Hans Nowak Points 1542

Je ne connais aucun module pour le faire. Si vous ne trouvez rien de ce genre dans le livre de recettes ou dans PyPI, vous pouvez essayer de le faire vous-même, en utilisant le module (non documenté) re.sre_parse. Cela pourrait vous aider à démarrer:

 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
 

5voto

Andrew Cox Points 5458

Tandis que les autres réponses utilisent le moteur de recherche pour analyser les éléments que j'ai moi-même corrigés, qui analysent la recherche et renvoient un modèle minimal qui correspondrait. (Notez qu'il ne gère pas les [^ annonces], les constructions de regroupement sophistiquées, les caractères spéciaux de début / fin de ligne). Je peux fournir les tests unitaires si vous aimez vraiment :)

 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
 

4voto

Adam Rosenfield Points 176408

À moins que votre expression rationnelle soit extrêmement simple (c'est-à-dire sans étoiles ni avantages), il y aura une infinité de chaînes qui lui correspondent. Si votre expression rationnelle implique uniquement la concaténation et l'alternance, vous pouvez développer chaque alternance dans toutes ses possibilités. Par exemple, (foo|bar)(baz|quux) peut être étendu dans la liste ['foobaz', 'fooquux', 'barbaz', 'barquux'] .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X