2012-04-09 13 views
2

Ich möchte mit regulären Ausdrücken Wörter in Gruppen von (vowels, not_vowels, more_vowels) unter Verwendung eines Markers aufteilen, um sicherzustellen, dass jedes Wort mit einem Vokal beginnt und endet.Reguläre Ausdrücke von Python: Erfassen von Lookahead-Werten (Erfassen von Text, ohne es zu konsumieren)

import re 

MARKER = "~" 
VOWELS = {"a", "e", "i", "o", "u", MARKER} 

word = "dog" 

if word[0] not in VOWELS: 
    word = MARKER+word 

if word[-1] not in VOWELS: 
    word += MARKER 

re.findall("([%]+)([^%]+)([%]+)".replace("%", "".join(VOWELS)), word) 

In diesem Beispiel erhalten wir:

[('~', 'd', 'o')] 

Das Problem ist, dass ich die Spiele wollen, überlappen - der letzte Satz von Vokalen sollte der erste Satz des nächsten Spiel werden. Dies scheint möglich, mit Lookaheads, wenn wir die Regex wie folgt ersetzen:

re.findall("([%]+)([^%]+)(?=[%]+)".replace("%", "".join(VOWELS)), word) 

Wir erhalten:

[('~', 'd'), ('o', 'g')] 

Was bedeutet, wir passen, was ich will. Es gibt jedoch jetzt nicht den letzten Satz von Vokalen zurück. Der Ausgang ich will, ist:

[('~', 'd', 'o'), ('o', 'g', '~')] 

Ich empfinde dies möglich sein soll (wenn die Regex für den zweiten Satz von Vokalen überprüfen, ich sehe keinen Grund, es nicht darauf zurückkommen kann), aber ich kann nicht finden, Jede Möglichkeit, dies über die Brute-Force-Methode hinaus zu tun, indem ich die Ergebnisse durchlaufe, nachdem ich sie gefunden habe, und das erste Zeichen der nächsten Übereinstimmung an die letzte Übereinstimmung anfüge und das letzte Zeichen der Zeichenfolge an die letzte Übereinstimmung. Gibt es einen besseren Weg, wie ich das machen kann?

Die zwei Dinge, die funktionieren würden, wäre die Erfassung des Lookahead-Wertes, oder nicht den Text auf einer Übereinstimmung zu konsumieren, während der Wert erfasst wird - ich kann auch keinen Weg finden.

+0

einen besonderen Grund, warum ich auf dies einen downvote bekam? –

Antwort

8

Ich fand es nur nach der Einlieferung:

re.findall("([%]+)([^%]+)(?=([%]+))".replace("%", "".join(VOWELS)), word) 

ein zusätzliches Paar von Klammern hinzufügen, die in der Look-Ahead bedeutet, dass es sich um eine Erfassung selbst wird.

Ich fand diese ziemlich obskur und schwer zu finden - ich bin nicht sicher, ob es nur jeder andere so offensichtlich gefunden hat, aber hoffentlich wird jeder andere in meiner Position dies in Zukunft leichter finden.

+1

Dies ist die Standardmethode, um überlappende Übereinstimmungen zu finden, BTW. – tchrist

+0

Ja, '(? ...)' Klammern fangen nicht mehr auf. Deshalb gibt es '(? :)' 'exists: so dass Sie explizit vermeiden können, etwas einzufangen, das noch gruppiert werden muss. In Ihrem Fall müssen Sie es trotzdem erfassen, also fügen Sie einfach einfangende Klammern hinzu. –

+0

Dies zeigt nur meinen Mangel an Regex-Wissen, denke ich. Gut, etwas Neues zu lernen. –

2

Ich würde nicht versuchen, die Regex-Engine dies zu tun; Ich würde die Zeichenfolge in Konsonanten- und Vokalabschnitte aufteilen und dann die überlappenden Ergebnisse erzeugen. Auf diese Weise müssen Sie auch nicht wirklich Marker einhacken, vorausgesetzt, Sie sind in Ordnung mit '' als "Vokal" -Teil, wenn das Wort nicht wirklich mit einem Vokal endet oder endet.

def overlapping_matches(word): 
    pieces = re.split('([^aeiou]+)', word) 
    # There are other ways to do this; I'm kinda showing off 
    return zip(pieces[:-2], pieces[1:-1], pieces[2:])[::2] 

overlapping_matches('dog') # [('', 'd', 'o'), ('o', 'g', '')] 

(Dies scheitert nach wie vor, wenn word nur Vokale enthält, aber der triviale ggf. korrigiert.)

+0

+1. Es ist eine nette Lösung, aber ich finde es nicht so lesbar wie die Lösung, die ich gefunden habe. –