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
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
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 –
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