2012-07-30 17 views
5

Sto cercando di creare un'espressione regolare VBA che troverà l'esistenza di due stringhe particolari all'interno di una serie di parentesi.Espressione regolare VBA: corrisponde un'espressione che non inizia con una parola specifica

Ad esempio, in questa espressione:

(aaa, bbb, ccc, ddd, aaa xxx)

si deve in qualche modo mi dicono che ha trovato entrambi "aaa" E "xxx aaa" nel espressione. Io, dato che c'è una corrispondenza su "aaa" senza "xxxx" davanti, e c'è anche una corrispondenza su "xxx aaa" più avanti nell'espressione, dovrebbe restituire true. Poiché queste due sequenze possono apparire in entrambi gli ordini, anche il contrario dovrebbe essere vero.

così sto pensando l'espressione/s sarebbe qualcosa di simile:

"(xxx aaa" [^ x] [^ x] [^ x] [^ x] aaa) "

di trovare le parole in un ordine e

"(aaa" [^ x] [^ x] [^ x] [^ x] xxx aaa)"

per le parole in un altro ordine.

Ha senso? O c'è un approccio migliore?

So che questo sta cambiando le specifiche, ma c'è un importante addendum - non ci possono essere parentesi di interceding tra i termini.

Così, per esempio, questo should't partita:

(aaa, bbb, ccc, ddd, (eee, aaa xxx))

In altre parole sto cercando di guardare in tra un solo il set di parentesi corrispondente.

+0

Riesci a rimuovere le parentesi e chiamare [ 'Split'] (http://msdn.microsoft.com/en-us/library/6x627e5f (v = vs80) .aspx) per separare le voci in una matrice che è possibile cercare? – mellamokb

+0

non potresti semplicemente usare la funzione InStr per questo? Potresti semplicemente usare una variabile booleana o qualcosa del genere e impostarla su true se trova una posizione per la frase che stai cercando nella stringa? Funzione InStr trovata qui: http://msdn.microsoft.com/en-us/library/8460tsh1(v=vs.80).aspx –

+0

Ho cercato di rispondere alla tua domanda il meglio possibile, ma non sei chiaro nel tuo definizione del problema. ** a) ** Regex non avrà mai una nozione di * "parentesi corrispondenti" *. È tecnicamente impossibile. ** b) ** Sembra che tu assuma che ',' sia una specie di separatore, ma non lo definisci mai veramente. – Tomalak

risposta

1

Zero larghezza asserttions look-ahead sono tuo amico.

Function FindInParen(str As String, term1 As String, term2 As String) As Boolean 
    Dim re As New VBScript_RegExp_55.RegExp 

    re.Pattern = "\(" & _ 
       "(?=[^()]*)\)" & _ 
       "(?=[^()]*\b" & RegexEscape(term1) & "\b)" & _ 
       "(?=[^()]*\b" & RegexEscape(term2) & "\b)" 

    FindInParen = re.Test(str) 
End Function 

Function RegexEscape(str As String) As String 
    With New VBScript_RegExp_55.RegExp 
    .Pattern = "[.+*?^$|\[\](){}\\]" 
    .Global = True 
    RegexEscape = .Replace(str, "\$&") 
    End With 
End Function 

Questo modello si legge come:

  • a partire da una parentesi di apertura, controllare:
    • che una chiusura corrispondente parentesi segue da qualche parte e non parentesi annidati all'interno
    • che term1 verifica prima che il chiusura paren
    • che si verifica prima del paren di chiusura

Dal momento che sto utilizzando look-ahead ((?=...)), il motore regex mai realmente si muove in avanti sulla corda, così posso catena come molte asserzioni look-ahead e averli tutti controllati.Un effetto collaterale è che l'ordine in cui si verificano term1 e term2 nella stringa non importa.

ho testato su console ("Finestra Immediata"):

? FindInParen("(aaa, bbb, ccc, ddd, xxx aaa)", "aaa", "xxx aaa") 
True 

? FindInParen("(aaa, bbb, ccc, ddd, (eee, xxx aaa))", "aaa", "xxx aaa") 
True 

? FindInParen("(aaa, bbb, ccc, ddd, (eee, xxx aaa))", "bbb", "xxx aaa") 
False 

Note:

  • rendimenti La seconda prova True perché tecnicamente, sia aaa e xxx aaa sono all'interno dello stesso insieme di parens.
  • Regex non può gestire strutture nidificate. Non riceverai mai parentesi nidificate con espressioni regolari. Non sarai mai in grado di trovare "un set di parenti corrispondente" con regex solo - solo una coppia di apertura/chiusura che non ha altri parenti nel mezzo. Scrivi un parser se hai bisogno di gestire il nesting.
  • Fare riferimento a "Microsoft VBScript Regular Expressions 5.5" nel progetto.

FWIW, ecco una funzione di nidificazione-aware minimo che funziona per il secondo caso di test di cui sopra:

Function FindInParen(str As String, term1 As String, term2 As String) As Boolean 
    Dim parenPair As New VBScript_RegExp_55.RegExp 
    Dim terms As New VBScript_RegExp_55.RegExp 
    Dim matches As VBScript_RegExp_55.MatchCollection 

    FindInParen = False 
    parenPair.Pattern = "\([^()]*\)" 
    terms.Pattern = "(?=.*?[(,]\s*(?=\b" & RegexEscape(Trim(term1)) & "\b))" & _ 
        "(?=.*?[(,]\s*(?=\b" & RegexEscape(Trim(term2)) & "\b))" 

    Do 
    Set matches = parenPair.Execute(str) 
    If matches.Count Then 
     If terms.Test(matches(0).Value) Then 
     Debug.Print "found here: " & matches(0).Value 
     FindInParen = True 
     End If 
     str = parenPair.Replace(str, "[...]") 
    End If 
    Loop Until FindInParen Or matches.Count = 0 

    If Not FindInParen Then 
    Debug.Print "not found" 
    End If 

    If InStr("(", str) > 0 Or InStr(")", str) > 0 Then 
    Debug.Print "mis-matched parens" 
    End If 
End Function 

Console:

? FindInParen("(aaa, bbb, ccc, ddd, (eee, xxx aaa))", "aaa", "xxx aaa") 
not found 
False 

? FindInParen("(aaa, bbb, ccc, ddd, (eee, xxx aaa))", "eee", "xxx aaa") 
found here: (eee, xxx aaa) 
True 
+0

Vorrei aggiungere globale (vedi risposta tim williams sotto) e scorrere tutti i termini corrispondenti. Molto utile, grazie. –

+0

@JackBeNimble L'aggiunta di "globale" a "terms.Pattern" non avrebbe senso, poiché restituisce sempre la stringa vuota. – Tomalak

+0

Ok, ma il secondo caso di test sopra restituisce false per me, su entrambe le iterazioni della funzione. Non so perché, è difficile capire le espressioni. –

1

In realtà non è chiaro dalla tua domanda esattamente ciò che si vuole (e forse Regexp non è realmente necessario qui), ma questo potrebbe essere vicino:

Sub Tester() 
    RegexpTest ("(aaa, bbb, ccc, ddd, xxx aaa)") 
End Sub 


Sub RegexpTest(txt As String) 
    Dim re As Object 
    Dim allMatches, m 

    Set re = CreateObject("VBScript.RegExp") 
    re.Pattern = "([^,\(]*aaa)" 
    re.ignorecase = True 
    re.Global = True 

    Set allMatches = re.Execute(txt) 

    For Each m In allMatches 
     Debug.Print Trim(m) 
    Next m 

End Sub 
Problemi correlati