2010-11-26 32 views
1

ich einen grundlegenden Text Template-Engine, die eine Syntax wie folgt verwendet:Regulärer Ausdruck, die Bilanzkreise verwendet

foo bar 
%IF MY_VAR 
    some text 
    %IF OTHER_VAR 
    some other text 
    %ENDIF 
%ENDIF 
bar foo 

Ich habe ein Problem mit dem regulären Ausdruck, den ich verwende es zu analysieren, wobei es nicht statt Berücksichtigung der verschachtelten IF/ENDIF-Blöcke.

Die aktuelle regex Ich verwende ist: %IF (?<Name>[\w_]+)(?<Contents>.*?)%ENDIF

Ich habe auf den Ausgleich Capture-Gruppen (eine Funktion von .NET regex Bibliothek) zu lesen, wie ich verstehen, das ist die empfohlene Methode „rekursiven“ regex ist der Unterstützung in .NET.

Ich spiele mit Gruppen Balancing und haben so kam weit mit dem Follow-up:

(
(
    (?'Open'%IF\s(?<Name>[\w_]+)) 
    (?<Contents>.*?) 
)+ 
(
    (?'Close-Open'%ENDIF)(?<Remainder>.*?) 
)+ 
)* 
(?(Open)(?!)) 

Aber dieses Verhalten ist nicht ganz, wie ich erwarten würde. Es fängt zum Beispiel viele leere Gruppen ein. Hilfe?

Antwort

5

Um eine ganze IF/ENDIF Block mit ausgeglichenem erfassen IF-Anweisungen können Sie diese Regex verwenden können:

%IF\s+(?<Name>\w+) 
(?<Contents> 
    (?> #Possessive group, so . will not match IF/ENDIF 
     \s| 
     (?<IF>%IF)|  #for IF, push 
     (?<-IF>%ENDIF)| #for ENDIF, pop 
     . # or, anything else, but don't allow 
    )+ 
    (?(IF)(?!)) #fail on extra open IFs 
) #/Contents 
%ENDIF 

Der Punkt hier ist dies: Sie kann nicht Capture in einem einzigen Match mehr als einer von jeder benannte Gruppe. Sie erhalten beispielsweise nur eine (?<Name>\w+) Gruppe des zuletzt erfassten Wertes. In meiner Regex, hielt ich die Name und Contents Gruppen von Ihrem einfachen Regex, und begrenzt die Auswuchtung in der Contents Gruppe - die Regex ist immer noch in IF und eingewickelt.

Wenn interessant wird, wenn Ihre Daten komplexer sind. Zum Beispiel:

%IF MY_VAR    
    some text 
    %IF OTHER_VAR 
    some other text 
    %ENDIF 
    %IF OTHER_VAR2 
    some other text 2 
    %ENDIF 
%ENDIF     
%IF OTHER_VAR3   
    some other text 3 
%ENDIF     

Hier finden Sie zwei Spiele erhalten, einen für MY_VAR und einen für OTHER_VAR3. Wenn Sie die beiden ifs auf MY_VAR Inhalt erfassen möchten, müssen Sie die Regex auf seiner Contents Gruppe erneut ausführen (Sie können umgehen, indem Sie ein Lookahead verwenden, wenn Sie müssen - wickeln Sie die gesamte Regex in (?=...), aber Sie müssen um es irgendwie in eine logische Struktur zu bringen, indem man Positionen und Längen benutzt).

Nun, ich werde nicht zu viel erklären, weil es scheint, Sie erhalten die Grundlagen, aber eine kurze Notiz über die Inhaltsgruppe - Ich habe eine Possessivgruppe verwendet, um Backtracking zu vermeiden. Sonst wäre es möglich, dass der Punkt schließlich mit IF s übereinstimmt und das Gleichgewicht bricht. Ein Lazy-Match für die Gruppe würde sich ähnlich verhalten (()+? anstelle von (?>)+).

+1

All dies beiseite, in Betracht ziehen, einen Parser zu verwenden, sollte es sich darum kümmern. – Kobi

+0

Das ist brilliant. Vielen Dank. Ich habe eine Rekursion in mein Programm eingefügt, so dass es die verschachtelten if/endif Blöcke durchquert. – nbevans

+0

Kleinere Ausgabe, die hoffentlich leicht zu beheben ist ... betrachte diesen Eingabetext: '% IF MY_VAR Text% IF OTHER_VAR% ENDIF'. Beachten Sie, dass die innere OTHER_VAR nicht mit einem ENDIF geschlossen ist. Die Regex stimmt jedoch mit dem inneren Block überein und nicht mit dem äußeren Block. Wie kann ich erreichen, dass die Regex in diesem speziellen Szenario (d. H. Fehlerhafte Vorlage) auf den äußersten Block passt und nicht direkt auf den inneren Block springt? – nbevans