2009-03-16 17 views
5

Sto provando a scrivere un'espressione regolare per identificare un'istruzione if. L'unico problema che sto avendo è catturarlo se le istruzioni che hanno parentesi nella loro parentesi. Per esempio:Espressione regolare per identificare istruzioni If

if (condition_function(params)) { 
    statements; 
} 

mia espressione di catturare tutti se le dichiarazioni tranne queste è:

if\s*\(([^\(\)]|\s)*\)\s*{(.|\s)*?} 

Qualcuno sa come scrivere questo?

+0

@Koukaakiva - La correzione dei posti per gli errori di ortografia è un comune qui su SO. Le FAQ contengono maggiori dettagli su questa pratica. http://stackoverflow.com/faq –

risposta

5

Penso che questo potrebbe funzionare. Se qualcuno vede qualcosa che io non, come una ragione per cui non funzionerà, per favore rispondi.

if\s*\(((?:[^\(\)]|\((?1)\))*+)\)\s*{((?:[^{}]|{(?2)})*+)} 

L'unico problema che si dovrebbe verificare ora è se c'è un'istruzione if in un'istruzione if.

Ho provato questo su ogni affermazione valida se posso pensare che potrebbe infrangerlo e l'unica cosa su cui non funziona è uno che contiene una stringa con una parentesi non abbinata.

Aggiornamento: ho trovato un errore con l'espressione regolare precedente. Non cattura se le istruzioni che contengono stringhe con parentesi non corrispondenti nelle loro condizioni o sezioni di istruzioni. Come nell'esempio seguente:

if (var1 == "("){ 
    echo "{"; 
} 

Tuttavia questa è una dichiarazione if valida.La soluzione:

if\s*\(((?:(?:(?:"(?:(?:\\")|[^"])*")|(?:'(?:(?:\\')|[^'])*'))|[^\(\)]|\((?1)\))*+)\)\s*{((?:(?:(?:"(?:(?:\\")|[^"])*")|(?:'(?:(?:\\')|[^'])*'))|[^{}]|{(?2)})*+)}\s* 

Questa espressione regolare cattura tutti, se le dichiarazioni anche quelli che contengono stringhe con parentesi senza eguali.

AGGIORNAMENTO: ora ce l'ho in modo da catturare il resto e altro se le istruzioni che sono allegate alle istruzioni if. L'unico problema è che i gruppi di cattura restituiti sono l'ultimo e l'ultimo altro se presente nell'istruzione if. Spero di riuscire a capire come aggirare anche quello.

if\s*\(((?:(?:(?:"(?:(?:\\")|[^"])*")|(?:'(?:(?:\\')|[^'])*'))|[^\(\)]|\((?1)\))*+)\)\s*{((?:(?:(?:"(?:(?:\\")|[^"])*")|(?:'(?:(?:\\')|[^'])*'))|[^{}]|{(?2)})*+)}\s*(?:(?:else\s*{((?:(?:(?:"(?:(?:\\")|[^"])*")|(?:'(?:(?:\\')|[^'])*'))|[^{}]|{(?3)})*+)}\s*)|(?:else\s*if\s*\(((?:(?:(?:"(?:(?:\\")|[^"])*")|(?:'(?:(?:\\')|[^'])*'))|[^\(\)]|\((?4)\))*+)\)\s*{((?:(?:(?:"(?:(?:\\")|[^"])*")|(?:'(?:(?:\\')|[^'])*'))|[^{}]|{(?5)})*+)}\s*))*; 

Se volete provarlo, ecco un grande sito per esso: http://gskinner.com/RegExr/

+1

Ancora non catturerai tutto, perché non stai usando un parser reale. Come un semplice esempio, questo corrisponderà a una stringa letterale come x = "if (x) {y;}"; – Ken

+0

Questo è esattamente quello che sto cercando di fare. Questo viene utilizzato per un motore di analisi alternativo per mediaWiki. Dal momento che trova se le affermazioni, sarò in grado di estrarle ed eseguirle come se fossero dichiarazioni nel mio codice. – Koukaakiva

+0

Incredibile. Ti darò +1 ma spero di non vederlo mai in alcun codice che devo toccare. Per punti extra, traducilo in brainf ** k – erikkallen

13

Questo non è possibile con le espressioni regolari in quanto le espressioni regolari possono abbinare solo linguaggi regolari e quello che si sta tentando di analizzare è context-free e non regolare (grazie a dirkgently e dmckee).

Dai un'occhiata alla WP: Formal language theory è che vi interessa ...

Btw. Non è nemmeno possibile controllare un'espressione composta da parentesi solo se è corretta ([[][]] è corretta ma non è []][) che è un "sottoproblema" di quello che hai fornito sopra.

+0

Ogni lingua normale è senza contesto. – dirkgently

+0

+1, ma aggiusta il nit che ha scelto in modo impercettibile ... – dmckee

+0

dirkgently, vero ma non viceversa. –

0

Se si deve usare un'espressione regolare, anche se non sarà mai catturare tutti i casi, questo è meglio:

if\s*\(((?!\s*\{).+)\)\s*\{(.|\s)*?\} 

Esso utilizza una positive lookahead ((?!\s*\{).), che garantiscano a catturare tutta fino alla chiusura ) (eccetto se il tuo stato condizionale contiene "{"!)

5

Stai provando a scrivere un'espressione regolare per analizzare una lingua non regolare? Non volerà mai.

+0

Se non sono espressioni regolari, cosa suggeriresti di analizzare questa lingua non regolare? – Koukaakiva

+0

Il compilatore utilizza un lexer seguito dall'analisi sintattica. Se hai solo bisogno di identificare le istruzioni if ​​e non puoi usare un parser esistente, puoi probabilmente scrivere un parser di discesa ricorsivo senza troppi problemi. – Ken

0

un rapido colpo a esso ...

if\s*?\(.*?)\s*?(({?\s*?(.*?;)+\s*?})|(.*?;)) 
1
r = /\bif\s*\(/ 

txt = <<TXT 
if(test) 
if (test) 
if (xyz) 
; if 
print x if(true) 
TXT 

p txt.scan(r) 

se (qualcosa) .. qualcosa può essere qualsiasi cosa .. se v'è una stringa con una parentesi fine al suo interno e si vuole affrontare correttamente con coppie di parentesi corrispondenti, quindi si finirà rapidamente con una grande regex.

Anche in che lingua stai cercando di competere?

+0

Sto provando a creare un parser per il motore mediaWiki che è più adatto al progetto su cui sto lavorando. Una delle cose che incontriamo continuamente è la necessità di dichiarazioni if. L'estensione mediaWiki per le istruzioni if ​​non corrisponde a quello che ci serve per entrambi. – Koukaakiva

+0

quindi se ho capito bene (non so mediawiki) .. hai bisogno di parti opzionali nel mark up, che possono essere abilitate/disabilitate? – neoneye

+0

se hai solo bisogno di estrarre se le affermazioni, allora potresti usare regex. Comunque per il compito penso che tu abbia bisogno di scrivere il tuo parser. Regex non può risolverlo da solo. Non è un compito facile, buona fortuna. – neoneye

3

è necessario scrivere codice in un linguaggio completo Turing. Esistono strumenti in grado di costruire automaticamente il codice per te, come ad esempio Flex. Tuttavia, se hai un semplice problema, è probabilmente più semplice scrivere semplicemente un semplice codice di analisi. Ecco alcuni esempi di codice C# che potrebbero aiutarti a iniziare.

public void TestIf() 
    { 
     var s = @"if (condition_function(params)) { 
    statements; 
     }"; 
     var ifRegex = @"if *\(.*\) *{.*}"; 
     if (Regex.IsMatch(s, ifRegex, RegexOptions.Singleline)) 
     { 
     var firstParens = s.IndexOf('('); 
     if (firstParens != -1) 
     { 
      var conditionPart = s.Skip(firstParens + 1); 
      int stack = 1; 
      int lastParens = -1; 
      while(stack > 0) 
      { 
      for (int i = 0; i < conditionPart.Count(); i++) 
      { 
       var c = conditionPart.ElementAt(i); 
       if (c == '(') 
       { 
       stack++; 
       } 
       if (c == ')') 
       { 
       stack--; 
       } 
       if (stack == 0) 
       { 
       lastParens = i; 
       break; 
       } 
      } 
      } 
      if (lastParens != -1) 
      { 
      var condition = conditionPart.Take(lastParens); 
      Console.WriteLine(new string(condition.ToArray())); 
      } 
     } 
     } 
    } 
+0

Questo è molto utile. Guardando altre porzioni del mio codice, mi rendo conto che potrei dover passare a qualcosa di simile. E questo codice probabilmente mi farà risparmiare un sacco di tempo. Grazie. – Koukaakiva

+0

E potrebbe anche essere necessario tenere conto di stringhe e commenti che possono contenere parentesi. – pro3carp3

+0

Probabilmente desideri eliminare i commenti in un passaggio precedente. Non dimenticare di occuparti di commenti annidati! – RossFabricant