Wie man Artikel Spinner regex machen?
Frage
Lassen Sie uns sagen, ich habe teh folgende:
{{Hallo | Hallo | Hey} {welt | Erde} | {Auf Wiedersehen | Abschied} {noobs | n3wbz | n00blets}}
Und ich möchte, dass in einer der folgenden Eigenschaften aktivieren:
Hello world
Goodbye noobs
Hi earth
farewell n3wbz
// etc.
Aufmerksamkeit auf die Art und Weise der „spinning“ Syntax verschachtelt ist. Es könnte eine Milliarde Schichten tief für alle verschachtelt werden wir wissen.
Ich kann das einfach tun, außer, wenn sie wie im obigen Beispiel verschachtelt sind meine regex vermasseln und die Ergebnisse sind nicht korrekt.
Könnte jemand zeigt ein Beispiel entweder in einer .NET-Sprache oder Python bitte?
Lösung
Eine einfache Art und Weise mit re.subn , die auch eine Funktion anstelle einer Ersatzzeichenfolge akzeptieren können:
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()
Es ist einfach ersetzt die tiefste Entscheidungen er trifft, dann iteriert, bis keine andere Wahl bleibt. subn
gibt ein Tupel mit dem Ergebnis, und wie viele Ersetzungen vorgenommen wurden, was sehr praktisch ist das Ende der Verarbeitung zu erkennen.
Meine Version von select()
kann durch bobince denen ersetzt werden, die random.choice()
verwenden und sind eleganter, wenn man nur auf einen zufälligen Wähler halten will. Wenn Sie die Wahl Baum bauen möchten, können Sie die obige Funktion erweitern, aber Sie werden globale Variablen brauchen, um zu verfolgen, wo Sie sind, so die Funktionen in eine Klasse bewegen würde Sinn machen. Dies ist nur ein Hinweis, ich will nicht, dass die Idee entwickeln, da es nicht wirklich die orginial Frage war.
Beachten Sie schließlich, dass Sie r.subn(select, s, re.U)
verwenden sollten, wenn Sie Unicode-Strings (s = u"{...}"
)
Beispiel:
>>> s = "{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}"
>>> print spinner(s)
'farewell n3wbz'
Edit: ersetzt sub
durch subn
Endlosschleife (dank bobince es, darauf hinzuweisen) zu vermeiden und machen sie effizienter und ersetzt {([^{}]+)}
durch {([^{}]*)}
leere geschweifte Klammern als auch zu extrahieren. Das sollte es robuster zu schlecht formatierten Muster.
Für Menschen, die so viel wie möglich zu setzen wie auf einer Linie (die ich persönlich würde nicht fördern):
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()
Andere Tipps
Sollte ziemlich einfach sein, nur eine Klammer nicht zuläßt aus mit einem anderen Satz, dann ruft immer wieder Ersatz von den inneren Streichhölzern dabei nach außen:
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'
Diese regex Inverter verwendet pyparsing erzeugen Strings (mit einigen Einschränkungen - unbegrenzte Wiederholung Symbole wie + und * sind nicht erlaubt) entsprechen. Wenn Sie ersetzen {} s mit () 's Ihre ursprünglichen Zeichenfolge in einen regulären Ausdruck zu machen, erzeugt der Wechselrichter diese Liste:
Helloworld
Helloearth
Hiworld
Hiearth
Heyworld
Heyearth
Goodbyenoobs
Goodbyen3wbz
Goodbyen00blets
farewellnoobs
farewelln3wbz
farewelln00blets
(Ich weiß, dass die Räume sind zusammengebrochen, aber vielleicht wird dieser Code gibt Ihnen einige Ideen, wie man dieses Problem attackieren.)
würde ich re.finditer verwenden und einen grundlegenden Parsing-Baum bauen, um die Verschachtelung Ebene zu bestimmen. Um es zu machen, würde ich die Spanne Attribut des Regex-Objekts verwenden:
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
Dies würde die Schachtelungstiefe der Klammerausdrücke geben. Fügen Sie einige ähnliche Logik für die Rohre und Sie würden gut auf dem Weg, das Problem zu lösen.
Ich würde empfehlen, einen Blick auf der dada Motor für Inspiration nehmen.
Ich habe eine Implementierung von etwas durch das in Schema inspiriert getan und AST Leveraged Schema meine Bedürfnisse auszudrücken.
Insbesondere würde ich empfehlen dringend davor, einen regulären Ausdruck als Parser im Allgemeinen zu verwenden.