2009-11-27 11 views
2

Disons que j'ai Teh suivantes:Comment faire un article spinner regex?

{{Bonjour | Salut | Hé} {monde | terre} | {Au revoir | adieu} {noobs | n3wbz | n00blets}}

Et je veux que de se transformer en une des opérations suivantes:

Hello world 
Goodbye noobs 
Hi earth 
farewell n3wbz 
// etc. 

Faire attention à la façon dont la syntaxe "spinning" est imbriqué Il pourrait être imbriqué un milliard de couches profondes pour tout ce que nous savons.

Je peux le faire facilement, sauf une fois qu'ils sont imbriqués, comme l'exemple ci-dessus, mon regex bousille et les résultats ne sont pas corrects.

Quelqu'un pourrait-il montrer un exemple dans un langage .NET ou Python s'il vous plaît?

+0

Comment décidez-vous lequel choisir? Randomness? – strager

+0

Oui par hasard – y2k

+0

Ou s'il y a une manière insensée de trouver par programme toutes les combinaisons qui seraient leet, mais pas nécessaire. – y2k

Répondre

5

Une façon simple avec re.subn, qui peut également accepter une fonction au lieu d'une chaîne de remplacement:

import re 
from random import randint 

def select(m): 
    choices = m.group(1).split('|') 
    return choices[randint(0, len(choices)-1)] 

def spinner(s): 
    r = re.compile('{([^{}]*)}') 
    while True: 
     s, n = r.subn(select, s) 
     if n == 0: break 
    return s.strip() 

Il remplace simplement tous les choix les plus profonds qu'il rencontre, alors pas d'autre choix que itère reste. subn renvoie un tuple avec le résultat et le nombre de remplacements effectués, ce qui est pratique pour détecter la fin du traitement.

Ma version de select() peut être remplacée par celle de Bobince qui utilise random.choice() et est plus élégante si vous voulez juste coller à un sélecteur aléatoire. Si vous voulez construire un arbre de choix, vous pouvez étendre la fonction ci-dessus, mais vous aurez besoin de variables globales pour garder une trace de l'endroit où vous êtes, donc déplacer les fonctions dans une classe aurait du sens. C'est juste un indice, je ne développerai pas cette idée puisque ce n'était pas vraiment la question orginale.

Notez enfin que vous devez utiliser r.subn(select, s, re.U) si vous avez besoin de chaînes unicode (s = u"{...}")

Exemple:

>>> s = "{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}" 
>>> print spinner(s) 
'farewell n3wbz' 

Edit: Remplacée sub par subn pour éviter boucle infinie (grâce à Bobince pour le signaler) et le rendre plus efficace, et remplacé {([^{}]+)} par {([^{}]*)} pour extraire vide cur ly également. Cela devrait le rendre plus robuste aux modèles mal formatés.

Pour les personnes qui aiment mettre autant que possible sur une ligne (que je personnellement n'encouragerais):

def spin(s): 
    while True: 
     s, n = re.subn('{([^{}]*)}', 
         lambda m: random.choice(m.group(1).split("|")), 
         s) 
     if n == 0: break 
    return s.strip() 
+1

snap (presque)! le test '{' irait dans une boucle infinie s'il y avait un {{'inégalé dans l'entrée, cependant. – bobince

+0

Vous avez raison, j'ai corrigé le code et rendu le test inutile. – RedGlyph

+0

doh, et je n'avais pas repéré subn, non plus! Eh bien, nous avons fini avec un programme acceptable entre nous :-) – bobince

1

Je voudrais utiliser re.finditer et construire un arbre d'analyse de base pour déterminer le niveau d'imbrication. Pour ce faire, je voudrais utiliser l'attribut span de l'objet match regex:

text = '{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}' 

import re 
re_bracks = re.compile(r'{.+?}') 

# subclass list for a basic tree datatype 
class bracks(list): 
    def __init__(self, m): 
     self.m = m 

# icky procedure to create the parse tree 
# I hate these but don't know how else to do it 
parse_tree = [] 
for m in re_bracks.finditer(text): 
    if not this_element: 
     # this first match 
     parse_tree.extend(element(m)) 
    else: 
     # ... and all the rest 
     this_element = bracks(m) 
     this_start, this_end = m.span() 

     # if this match is nested in the old one ... 
     if this_start < previous_start and this_end > previous_end: 
      # nest it inside the previous one 
      previous_element.extend(this_element) 
     else: 
      # otherwise make it a child of the parse_tree 
      parse_tree.extend(element(m)) 

     previous_element = this_element 
     previous_start, previous_end = this_start, this_end 

Cela vous donne la profondeur d'imbrication des expressions entre crochets. Ajouter une logique similaire pour les tuyaux et vous seriez sur la bonne voie pour résoudre le problème.

+0

Oops, s'est rendu compte que cette regex ne fonctionnerait pas. – twneale

1

Je vous recommande de jeter un oeil à the dada engine pour l'inspiration.

J'ai fait une implémentation de quelque chose qui s'inspire de cela dans le schéma AST et le système à effet de levier pour exprimer mes besoins. En particulier, je recommande fortement de ne pas essayer d'utiliser un regex comme analyseur en général.

4

devrait être assez simple, désavouer juste un corset mis d'inclure une autre, puis à plusieurs reprises appellent à faire des remplacements des matchs internes vers l'extérieur:

def replacebrace(match): 
    return random.choice(match.group(1).split('|')) 

def randomizebraces(s): 
    while True: 
     s1= re.sub(r'\{([^{}]*)\}', replacebrace, s) 
     if s1==s: 
      return s 
     s= s1 

>>> randomizebraces('{{Hello|Hi|Hey} {world|earth}|{Goodbye|farewell} {noobs|n3wbz|n00blets}}') 
'Hey world' 
>>> randomizebraces('{{Hello|Hi|Hey} {world|earth}|{Goodbye|farewell} {noobs|n3wbz|n00blets}}') 
'Goodbye noobs' 
+0

+1: gentil, je ne savais pas random.choice. J'ai hésité à utiliser la comparaison de chaînes, mais j'ai opté pour la simple recherche entre parenthèses/regexp compilé pour le rendre un peu plus rapide (mais moins sûr). – RedGlyph

2

Cette regex inverter utilise pyparsing pour générer des chaînes correspondant (avec certaines restrictions - symboles de répétition illimitée comme + et * ne sont pas autorisés). Si vous remplacez {} s « s avec() » pour faire de votre chaîne d'origine dans un regex, l'onduleur génère cette liste:

Helloworld 
Helloearth 
Hiworld 
Hiearth 
Heyworld 
Heyearth 
Goodbyenoobs 
Goodbyen3wbz 
Goodbyen00blets 
farewellnoobs 
farewelln3wbz 
farewelln00blets 

(je sais que les espaces sont réduits, mais peut-être ce code vous donnera quelques idées sur la façon d'attaquer ce problème.)