RegEx Only如果两个单词之间存在单词,则返回匹配项

时间:2013-10-07 19:22:06

标签: regex vba vbscript

我有一个大型设备配置文件,我正在尝试使用RegEx解析相关部分以进行进一步编码...我试图解析的Config部分将以“edit ServiceName;模式“并将以”退出“一词结束。此配置文件和返回的字符串将在多行上。我只想返回或匹配此配置文件中包含某些关键字的某些部分...

Sub TestRegEx_1()
Dim TestString
Dim objRegEx, f_objResults, f_Match

TestString = "edit NonMatch1 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit NonMatch2 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit GoodMatch1 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "KeyWord_1 1 2 and 3" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit GoodMatch2 ;mode" & vbCrLf & _
    "KeyWord_2 A B and C" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit NonMatch3 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit GoodMatch3 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "KeyWord_3 1A" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "exit"

Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.IgnoreCase = True
objRegEx.MultiLine = True
objRegEx.Global = True

objRegEx.Pattern = "^edit (.{0,}) \;mode[\s\S]*?" & _
 "(?=(KeyWord_1|KeyWord_2|KeyWord_3))[\s\S]*?exit$"

Set f_objResults = objRegEx.Execute(TestString)
For Each f_Match In f_objResults
    MsgBox f_Match.Value
Next
End Sub

因为RegEx很贪婪,上面的例程会返回一个包含我不想要的部分的匹配。我能够将我的例程分成两个单独的RegEx模式搜索,以使其正常运行,但我想修改我的初始模式搜索,以便我不必这样做。 Below例程将创建我正在寻找的输出。

Sub TestRegEx_2()
Dim TestString
Dim objRegEx, f_objResults, f_Match

TestString = "edit NonMatch1 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit NonMatch2 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit GoodMatch1 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "KeyWord_1 1 2 and 3" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit GoodMatch2 ;mode" & vbCrLf & _
    "KeyWord_2 A B and C" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit NonMatch3 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "exit" & vbCrLf & _
    "edit GoodMatch3 ;mode" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "KeyWord_3 1A" & vbCrLf & _
    "Something Random" & vbCrLf & "Something Random" & vbCrLf & _
    "exit"

Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.IgnoreCase = True
objRegEx.MultiLine = True
objRegEx.Global = True

'This Works...
objRegEx.Pattern = "^edit (.{0,}) \;mode[\s\S]*?exit$"
Set f_objResults = objRegEx.Execute(TestString)

objRegEx.Pattern = "(?=(KeyWord_1|KeyWord_2|KeyWord_3))"
For Each f_Match In f_objResults
    If objRegEx.test(f_Match.Value) Then
        MsgBox f_Match.Value
    End If
Next

End Sub

在初始模式匹配中需要更改哪些内容才能在不创建单独的RegEx模式的情况下进行此操作?如何在第一个“退出”实例后明确告诉RegEx引擎停止,这样如果找不到匹配项,在找到匹配项之前不会继续包含其他字符串?任何帮助是极大的赞赏!谢谢。

编辑:添加了我希望由Match返回的测试字符串中的部分。 “GoodMatch”部分可以包含一个或多个关键字。我需要返回完整的部分。

edit GoodMatch1 ;mode
Something Random
Something Random
KeyWord_1 1 2 and 3
exit

edit GoodMatch2 ;mode
KeyWord_2 A B and C
Something Random
Something Random
exit

edit GoodMatch3 ;mode
Something Random
Something Random
KeyWord_3 1A
Something Random
Something Random
exit

3 个答案:

答案 0 :(得分:4)

我不确定您的完整配置文件是怎样的,但您可以尝试类似:

(KeyWord_1|KeyWord_2|KeyWord_3)(?=(?:(?!edit)[\s\S])*?exit)

这只会在'编辑...退出'块中匹配。

或者:

(KeyWord_1|KeyWord_2|KeyWord_3)(?=(?:(?!edit[^;]+;mode )[\s\S])*?exit)

对于特定的“编辑...;模式...退出”块。

前瞻是迫使比赛在'编辑...退出'区块内,基本上是确保在下一个'退出'之前没有'编辑'。如果你在一个区块内,那么两者之间将没有“编辑”,因此匹配。如果你在外面,你必须在'退出'之前点击'编辑',因此不匹配。


编辑:要获得整个块,您可以使用:

edit(?=(?:(?!exit)[\S\s])*\b(KeyWord_1|KeyWord_2|KeyWord_3)\b)(?:(?!exit)[\S\s])*exit

匹配本身就是块,子匹配是关键字。

答案 1 :(得分:1)

你的正则表达并不贪心,但你已经成为对非贪婪比赛的常见误解的牺牲品。那些会产生最短的匹配,但是非贪婪之后当前光标位置到表达式的下一次出现的匹配(子)的表达。

让我们来看看(部分)测试字符串:

edit NonMatch1 ;mode
Something Random
Something Random
exit
edit NonMatch2 ;mode
Something Random
exit
edit GoodMatch1 ;mode
Something Random
Something Random
KeyWord_1 1 2 and 3
exit
edit GoodMatch2 ;mode
KeyWord_2 A B and C
Something Random
Something Random
exit

第一场比赛你想要的是:

edit NonMatch1 ;mode
Something Random
Something Random
exit
edit NonMatch2 ;mode
Something Random
exit
edit GoodMatch1 ;mode
Something Random
Something Random
KeyWord_1 1 2 and 3
exit
edit GoodMatch2 ;mode
KeyWord_2 A B and C
Something Random
Something Random
exit

但实际得到的是:

edit NonMatch1 ;mode
Something Random
Something Random
exit
edit NonMatch2 ;mode
Something Random
exit
edit GoodMatch1 ;mode
Something Random
Something Random
KeyWord_1 1 2 and 3
exit
edit GoodMatch2 ;mode
KeyWord_2 A B and C
Something Random
Something Random
exit

原因是当正则表达式解析器开始读取您的字符串时,第一行与表达式的第一部分(^edit (.{0,}) \;mode)匹配。然后,expresseion的下一部分([\s\S]*?(?=(KeyWord_1|KeyWord_2|KeyWord_3)))匹配从该行末尾的换行符到三个关键字之一的第一个出现的所有内容,从而跨越多个edit部分。

对您的问题最简单的解决方案可能是使用正则表达式将字符串无差别地划分为编辑部分,然后使用字符串匹配来选择所需的字符串:

testString = "..."

Set re = New RegExp
re.IgnoreCase = True
re.MultiLine  = True
re.Global     = True
re.Pattern    = "^edit (.*) \;mode[\s\S]*?exit$"

For Each m In re.Execute(testString)
  If InStr(m.Value, "KeyWord_1") > 0 Then
    'do some
  ElseIf InStr(m.Value, "KeyWord_2") > 0 Then
    'do other
  ElseIf InStr(m.Value, "KeyWord_3") > 0 Then
    'do something completely different
  End If
Next

当然你也可以在循环中使用另一个正则表达式:

testString = "..."

Set re = New RegExp
re.IgnoreCase = True
re.MultiLine  = True
re.Global     = True
re.Pattern    = "^edit (.*) \;mode[\s\S]*?exit$"

Set keywords = New RegExp
keywords.IgnoreCase = True
keywords.Pattern    = "keyword_1|keyword_2|keyword_3"

For Each m In re.Execute(testString)
  If keywords.Test(m.Value) Then
    WScript.Echo m.Value
  End If
Next

答案 2 :(得分:0)

你需要懒惰,这是什么?

http://www.regular-expressions.info/repeat.html