Question

Disons que j'ai ce qui suit:

  

{{Bonjour | Bonjour | Hey} {monde | terre} |   {Au revoir | adieu} {noobs | n3wbz | n00blets}}

Et je souhaite que cela se transforme en l'un des éléments suivants:

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

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

Je peux le faire facilement, sauf que, une fois imbriqués, comme dans l'exemple ci-dessus, mon regex se gâte et que 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?

Était-ce utile?

La solution

Une méthode 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, puis itère jusqu'à ce qu'il ne reste plus de choix. 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 souhaitez vous en tenir à un sélecteur aléatoire. Si vous souhaitez créer un arbre de choix, vous pouvez étendre la fonction ci-dessus, mais vous aurez besoin de variables globales pour garder une trace de votre position. Transférer les fonctions dans une classe aurait donc un sens. C'est juste un indice, je ne développerai pas cette idée car 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'

Modifier: Remplacé sub par subn afin d'éviter une boucle infinie (merci à Bobince de le signaler) et de la rendre plus efficace, et {([^ {}] +)} a été remplacé par {([^ {}] *)} pour extraire également les accolades vides. Cela devrait le rendre plus résistant aux modèles mal formatés.

Pour les personnes qui aiment mettre autant que possible sur une seule ligne (ce que je ne recommanderais pas personnellement):

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()

Autres conseils

Devrait être assez simple, il suffit d'empêcher un ensemble de corset d'inclure un autre, puis d'appeler plusieurs fois en effectuant des remplacements à partir des correspondances internes:

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'

Cet inverseur de regex utilise pyparsing pour générer des chaînes correspondantes (avec certaines restrictions - les symboles de répétition illimités tels que + et * ne sont pas autorisés). Si vous remplacez {} par () pour transformer votre chaîne d'origine en regex, le convertisseur génère cette liste:

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

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

Je voudrais utiliser re.finditer et créer un arbre d’analyse de base pour déterminer le niveau d’imbrication. Pour ce faire, j'utiliserais l'attribut span de l'objet de correspondance 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 donnerait la profondeur d'imbrication des expressions entre crochets. Ajoutez une logique similaire pour les tuyaux et vous seriez sur la bonne voie pour résoudre le problème.

Je vous conseillerais de jeter un coup d'œil sur le moteur dada .

J'ai implémenté quelque chose qui a inspiré cela dans le schéma et utilisé l'AST du schéma pour exprimer mes besoins.

Plus précisément, je vous déconseille vivement d'utiliser une expression rationnelle comme analyseur syntaxique en général.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top