2011-10-26 14 views
26

Sto tentando di creare un'espressione .NET RegEx che bilancia correttamente la mia parentesi. Ho la seguente espressione RegEx:Utilizzo di RegEx per bilanciare la parentesi di corrispondenza

func([a-zA-Z_][a-zA-Z0-9_]*)\(.*\) 

La stringa che sto cercando di abbinare è questo:

"test -> funcPow((3),2) * (9+1)" 

Quello che dovrebbe accadere è Regex deve corrispondere tutto dal funcPow fino alla seconda parentesi di chiusura. Dovrebbe fermarsi dopo la seconda parentesi di chiusura. Invece, si abbina fino all'ultimo parentesi di chiusura. RegEx sta tornando in questo modo:

"funcPow((3),2) * (9+1)" 

deve restituire questo:

"funcPow((3),2)" 

Qualsiasi aiuto su questo sarebbe apprezzato.

risposta

36

espressioni regolari possono sicuramente fare corrispondenza tra parentesi bilanciata. Può essere complicato e richiede un paio di funzioni Regex più avanzate, ma non è troppo difficile.

Esempio:

var r = new Regex(@" 
    func([a-zA-Z_][a-zA-Z0-9_]*) # The func name 

    \(      # First '(' 
     (?:     
     [^()]    # Match all non-braces 
     | 
     (?<open> \()  # Match '(', and capture into 'open' 
     | 
     (?<-open> \))  # Match ')', and delete the 'open' capture 
     )+ 
     (?(open)(?!))  # Fails if 'open' stack isn't empty! 

    \)      # Last ')' 
", RegexOptions.IgnorePatternWhitespace); 

bilanciati gruppo combaciante ha un paio di caratteristiche, ma per questo esempio, si sta utilizzando solo la cattura la cancellazione funzione. La riga (?<-open> \)) corrisponderà a ) ed eliminerà la precedente acquisizione "aperta".

La linea più delicata è (?(open)(?!)), quindi lasciatemi spiegare. (?(open) è un'espressione condizionale che corrisponde solo se è presente un'acquisizione "aperta". (?!) è un'espressione negativa che fallisce sempre. Pertanto, (?(open)(?!)) dice "se c'è un'acquisizione aperta, quindi non riesce".

Microsoft's documentation era abbastanza utile anche.

+0

Ho cambiato la riga '[^()] * # Corrisponde a tutte le non parentesi 'in modo che corrisponda() con niente all'interno di –

+0

nota' ''. Questo funziona benissimo! –

-1

Le espressioni regolari funzionano solo su Regular Languages. Ciò significa che un espressione regolare può trovare cose del genere "qualsiasi combinazione di A e B è". (ab o babbabaaa ecc), ma non riesce a trovare "n a di, una b, n una di". (a^n b a^n) Le espressioni regolari non possono garantire che il primo set di a corrisponda al secondo set di a.

Per questo motivo, non sono in grado di eguagliare un numero uguale di parentesi di apertura e chiusura. Sarebbe abbastanza facile scrivere una funzione che attraversi la stringa un carattere alla volta. Avere due contatori, uno per aprire il paren, uno per la chiusura. incrementare i puntatori mentre si attraversa la stringa, se opening_paren_count != closing_parent_count restituisce false.

+6

Ecco come può essere, ma ** ** espressioni regolari può essere utilizzato su quasi ogni tipo di testo fino a quando si capisce i loro limiti. Gli schemi ricorsivi/equilibrati sono brutti e (IMO) raramente vale lo sforzo, ma sono * supportati da molti sapori regex. –

-1
func[a-zA-Z0-9_]*\((([^()])|(\([^()]*\)))*\) 

È possibile utilizzare questo, ma se si sta lavorando con. NET, ci possono essere alternative migliori.

Questa parte voi già sanno:

func[a-zA-Z0-9_]*\(--weird part-- \) 

La parte part-- --weird significa solo; ( consentire qualsiasi carattere . o | qualsiasi sezione (.*) esistere tutte le volte che desidera )*. L'unico problema è che non è possibile abbinare qualsiasi carattere ., è necessario utilizzare [^()] per escludere la parentesi.

(([^()])|(\([^()]*\)))* 
+0

È necessario specificare che questo funzionerà con un solo livello di nidificazione. –

+0

@ScottRippey: Funziona ancora se esiste una funzione all'interno di quella funzione. Il | la condizione lo gestisce Puoi fare un esempio in cui questa regex fornisce una corrispondenza falsa? – rkw

+1

Funziona correttamente e fa esattamente come richiesto dall'OP, quindi è una buona risposta. Tuttavia, è difficile codificare solo un livello di nidificazione, quindi non corrisponderà: 'func (a (b (c) d) e)'. Non è chiaro se l'OP avesse bisogno di questo. –

18

Utilizzando gruppi bilanciati, che è:

Regex rx = new Regex(@"func([a-zA-Z_][a-zA-Z0-9_]*)\(((?<BR>\()|(?<-BR>\))|[^()]*)+\)"); 

var match = rx.Match("funcPow((3),2) * (9+1)"); 

var str = match.Value; // funcPow((3),2) 

(?<BR>\()|(?<-BR>\)) sono una Balancing Group (la BR che ho usato per il nome è per Brackets). E 'più chiaro in questo modo (?<BR> \ ()|(?<-BR> \) ) forse, in modo che il \( e \) sono più "evidenti".

Se davvero odi (e il mondo/i tuoi compagni di co-programmatori) a sufficienza per usare queste cose, io suggerisco di usare il "spolverata" spazio bianco RegexOptions.IgnorePatternWhitespace e ovunque :-)

+0

Penso che manchi l'ultima parte critica, '(? (BR) (?!))' –

+0

@ScottRippey No. Ci sono altre espressioni dopo la chiusura ')'. La domanda dell'OP era MOLTO precisa. Vuole 'funcsomething()', non per analizzare l'intera espressione. Quindi la prima parentesi "sbilanciata" che trovo è la parentesi chiusa della mia sottoespressione. 'funcPow ((3), 2) * (9 + 1) -> funcPow ((3), 2)' – xanatos

+1

Oh, ho capito che '(? (BR) (?!))' serve solo a garantire l'apertura la coppia ha una parentesi di chiusura. sito Web di Microsoft: "(? (Open) (?!)) Il subexpression finale, indica se i costrutti di nidificazione nella stringa di input sono correttamente bilanciati" –

Problemi correlati