2012-07-30 16 views
5

Ich bin auf der Suche nach einem regulären Ausdruck VBA, die die Existenz von zwei bestimmten Zeichenfolgen innerhalb einer Reihe von Klammern finden wird.VBA Regex - passen Sie einen Ausdruck, der nicht mit einem bestimmten Wort beginnt

Zum Beispiel in diesem Ausdruck:

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

sollte es mir irgendwie sagen, dass es beide "aaa" gefunden und "xxx aaa" in der Ausdruck. D. h., Es gibt eine Übereinstimmung auf "aaa" ohne die "xxxx" in der Front, und es gibt auch eine Übereinstimmung auf "xxx aaa" später im Ausdruck, sollte es wahr zurückgeben. Da diese beiden Sequenzen in jeder Reihenfolge auftreten können, sollte auch das Gegenteil der Fall sein.

Ich denke, den Ausdruck So/s würde wie folgt sein:

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

um die Worte in einem Auftrag zu finden und

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

für die Wörter in einer anderen Reihenfolge.

Macht das Sinn? Oder gibt es einen besseren Ansatz?

Ich weiß, dass dies die Spezifikation ändert, aber es gibt ein wichtiges Addendum - zwischen den Begriffen dürfen keine dazwischenliegenden Klammern stehen.

So zum Beispiel, should't dieses Spiel:

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

Mit anderen Worten: Ich versuche, zwischen einem suchen in nur passende Klammern.

+0

Können Sie die Klammern entfernen und rufen [ 'Split'] (http://msdn.microsoft.com/en-us/library/6x627e5f (v = vs.80) .aspx) um die Einträge in ein Array zu trennen, das Sie durchsuchen können? – mellamokb

+0

können Sie nicht einfach die InStr-Funktion dafür verwenden? Sie könnten einfach eine boolesche Variable oder etwas verwenden und sie auf "true" setzen, wenn sie einen Ort für die gesuchte Phrase in der Zeichenfolge findet? InStr Funktion hier gefunden: http://msdn.microsoft.com/en-us/library/8460tsh1(v=vs.80).aspx –

+0

Ich habe versucht, Ihre Frage bestmöglich zu beantworten, aber Sie sind unklar in Ihrem Problem Definition. ** a) ** Regex wird niemals eine Vorstellung von * "passenden Klammern" * haben. Es ist technisch unmöglich. ** b) ** Sie scheinen davon auszugehen, dass "," eine Art Trennzeichen ist, aber Sie definieren das nie wirklich. – Tomalak

Antwort

1

Null-Breite Vorgriff asserttions Ihr Freund ist.

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 

Dieses Muster lautet:

  • von einer Öffnung paren starten, überprüfen:
    • die einen passenden Schließ paren irgendwo und keine verschachtelten Pars innerhalb
    • folgt, dass term1 vor dem auftritt Schließen paren
    • , dass term2 vor dem Schließen auftritt

Da ich Vorgriffs ((?=...)) verwendet wird, bewegt sich nie die Regex-Engine tatsächlich vorwärts auf der Schnur, so kann ich Kette so viele Vorgriffs Behauptungen und haben sie alle überprüft.Ein Nebeneffekt ist, dass die Reihenfolge, in der term1 und term2 in der Zeichenfolge auftreten, keine Rolle spielt.

I testete ihn auf der Konsole ("Immediate Window"):

? 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 

Anmerkungen:

  • Der zweite Test-Ausbeuten True da-technisch sowohl aaa und xxx aaa innerhalb desselben Satzes sind von Parens.
  • Regex kann nicht mit verschachtelten Strukturen umgehen. Mit regulären Ausdrücken erhalten Sie nie geschachtelte Klammern. Sie werden nie "ein passender Satz von Parens" mit Regex allein finden - nur ein öffnendes/schließendes Paar, das keine anderen Paren dazwischen hat. Schreiben Sie einen Parser, wenn Sie mit der Verschachtelung umgehen müssen.
  • Machen Sie einen Verweis auf "Microsoft VBScript Regular Expressions 5.5" in Ihrem Projekt.

FWIW, hier ist eine minimale Verschachtelung-aware-Funktion, die oben für den zweiten Testfall funktioniert:

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

Ich würde global hinzufügen (siehe tim williams Antwort unten) und durchlaufen alle übereinstimmenden Begriffe. Sehr hilfreich, danke. –

+0

@JackBeNimble Das Hinzufügen von "global" zu den 'terms.Pattern' würde keinen Sinn ergeben, da immer die leere Zeichenfolge zurückgegeben wird. – Tomalak

+0

Ok, aber der zweite obige Testfall gibt für mich bei beiden Iterationen der Funktion den Wert false zurück. Nicht sicher, warum, es ist schwer, die Ausdrücke herauszufinden. –

1

Es ist nicht wirklich klar, aus Ihrer Frage genau das, was Sie wollen (und vielleicht ist Regexp nicht wirklich hier erforderlich), aber dies könnte nahe sein:

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 
Verwandte Themen