2012-07-30 40 views
5

Tôi đang tìm cách tạo biểu thức chính quy VBA sẽ tìm thấy sự tồn tại của hai chuỗi cụ thể bên trong một tập hợp các dấu ngoặc đơn.VBA regex - khớp với cụm từ không bắt đầu bằng một từ cụ thể

Ví dụ, trong biểu thức này:

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

nó bằng cách nào đó nên nói với tôi rằng nó tìm thấy cả hai "aaa" VÀ "xxx aaa" trong biểu hiện. I.e, vì có một trận đấu trên "aaa" không có "xxxx" ở phía trước, và cũng có một trận đấu trên "xxx aaa" sau này trong biểu thức, nó sẽ trả về true. Vì hai trình tự này có thể xuất hiện theo thứ tự, nên ngược lại cũng đúng.

Vì vậy, tôi đang nghĩ đến sự biểu hiện/s sẽ là một cái gì đó như thế này:

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

để tìm các từ trong một trật tự và

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

cho các từ theo thứ tự khác.

Điều này có hợp lý không? Hoặc là có một cách tiếp cận tốt hơn?

Tôi biết điều này đang thay đổi thông số kỹ thuật, nhưng có một phụ lục quan trọng - không thể có bất kỳ dấu ngoặc đơn xen kẽ nào giữa các cụm từ.

Vì vậy, ví dụ, điều này phù hợp với should't:

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

Nói cách khác Tôi đang cố gắng để tìm trong giữa một chỉ tập hợp các dấu ngoặc đơn.

+0

Bạn có thể loại bỏ các dấu ngoặc đơn và gọi [ 'Split'] (http://msdn.microsoft.com/en-us/library/6x627e5f (v = vs.80) .aspx) để tách các mục nhập thành một mảng mà bạn có thể tìm kiếm? – mellamokb

+0

bạn không thể sử dụng chức năng InStr cho điều này? Bạn chỉ có thể sử dụng một biến boolean hoặc một cái gì đó và đặt nó thành true nếu nó tìm thấy một vị trí cho cụm từ bạn đang tìm kiếm trong chuỗi? Chức năng InStr được tìm thấy tại đây: http://msdn.microsoft.com/en-us/library/8460tsh1(v=vs.80).aspx –

+0

Tôi đã cố gắng trả lời câu hỏi của bạn tốt nhất có thể, nhưng bạn không rõ ràng trong định nghĩa vấn đề. ** a) ** Regex sẽ không bao giờ có khái niệm * "dấu ngoặc đơn phù hợp" *. Đó là kỹ thuật không thể. ** b) ** Bạn có vẻ giả định rằng ',' là một loại dấu tách, nhưng bạn không bao giờ thực sự xác định điều đó. – Tomalak

Trả lời

1

Zero-width nhìn về phía trước asserttions là bạn của bạn.

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 

mẫu này lần đọc như:

  • Bắt đầu từ một dấu ngoặc mở, kiểm tra:
    • rằng một paren khớp đóng cửa sau đâu đó và không có dấu ngoặc lồng nhau bên
    • rằng term1 xảy ra trước khi đóng paren
    • rằng term2 xảy ra trước khi đóng ngoặc đơn

Kể từ khi tôi đang sử dụng nhìn về phía trước ((?=...)), động cơ regex bao giờ thực sự di chuyển về phía trước trên chuỗi, vì vậy tôi có thể chuỗi như nhiều khẳng định nhìn về phía trước và tất cả đều được kiểm tra.Một tác dụng phụ là thứ tự trong đó term1term2 xảy ra trong chuỗi không quan trọng.

Tôi đã thử nghiệm nó trên giao diện điều khiển ("Ngay lập tức cửa sổ"):

? 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 

Ghi chú:

  • Các thử nghiệm thứ hai sản lượng True vì-kỹ thuật-cả aaaxxx aaa đang ở trong cùng một tập của parens.
  • Regex không thể xử lý các cấu trúc lồng nhau. Bạn sẽ không bao giờ nhận được dấu ngoặc đơn lồng nhau ngay với các biểu thức thông thường. Bạn sẽ không bao giờ có thể tìm thấy "một tập hợp các dấu ngoặc đơn" chỉ với một mình regex - chỉ một cặp mở/đóng không có dấu ngoặc kép khác ở giữa. Viết một trình phân tích cú pháp nếu bạn cần xử lý lồng nhau.
  • Tham chiếu đến "Microsoft VBScript Regular Expressions 5.5" trong dự án của bạn.

FWIW, đây là một chức năng làm tổ-aware tối thiểu mà làm việc cho các trường hợp thử nghiệm thứ hai ở trên:

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

Tôi sẽ thêm toàn cầu (xem tim williams trả lời dưới đây) và lặp qua tất cả các điều khoản phù hợp. Rất hữu ích, cảm ơn. –

+0

@JackBeNimble Việc thêm "toàn cầu" vào 'terms.Pattern' sẽ không có ý nghĩa, vì nó luôn trả về chuỗi rỗng. – Tomalak

+0

Ok, nhưng trường hợp thử nghiệm thứ hai ở trên trả về false cho tôi, trên cả hai lần lặp của hàm. Không chắc chắn tại sao, thật khó để tìm ra các biểu thức. –

1

Nó không thực sự rõ ràng từ câu hỏi của bạn chính xác những gì bạn muốn (và có thể biểu thức chính quy là không thực sự cần thiết ở đây) nhưng điều này có thể được gần gũi:

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 
Các vấn đề liên quan