【问题标题】:RegEx Only Return matches if words are present between two words仅当两个单词之间存在单词时,RegEx 才返回匹配项
【发布时间】:2023-04-07 03:10:01
【问题描述】:

我有一个大型设备配置文件,我正在尝试使用 RegEx 解析出相关部分以进行进一步编码...我尝试解析的配置部分将以 "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 模式搜索以使其正常运行,但我想修改我的初始模式搜索,这样我就不必这样做了。下面的例程将创建我正在寻找的输出。

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 引擎在“exit”的第一个实例之后停止,以便如果它没有找到匹配项,它不会继续包含其他字符串,直到找到匹配项?任何帮助是极大的赞赏!谢谢。

编辑:添加了我希望匹配返回的测试字符串中的部分。 “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

【问题讨论】:

    标签: regex vba vbscript


    【解决方案1】:

    我不确定您的完整配置文件是什么样的,但您可以尝试以下操作:

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

    这将仅在“编辑...退出”块内匹配。

    或者:

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

    对于特定的 'edit ... ;mode ... exit' 块。

    前瞻是强制匹配在“编辑...退出”块内的原因,基本上是通过确保在下一个“退出”之前没有“编辑”。如果您在一个块内,则两者之间不会有“编辑”,因此会有匹配。如果您在外面,则必须在“退出”之前点击“编辑”,因此不匹配。


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

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

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

    【讨论】:

    • 我喜欢这个,但我需要返回“edit”和“exit”之间的完整字符串。有什么建议吗?
    • @BHart 当然,我添加了一个不同的正则表达式;这是获取块的不同方法。 regex101 demo(如果你想要一个演示站点,看看比赛是如何发生的:))
    • 杰瑞 - 你像 KISS 一样摇滚!!!太感谢了。希望我有更多代表支持您的答案...感谢您为提供这个非常有用的网站付出了额外的努力。
    • @BHart 嘿,没关系 xD 我很高兴你的问题(至少这部分,因为我不知道大局)得到了解决,这足以让我每天开车=P
    • 是的,配置文件是一个更大的混乱的一部分,但我能够将这一行修改为我需要的工作:D 感谢您的帮助和良好的态度!
    【解决方案2】:

    您的正则表达式并不贪心,但您已成为对非贪心匹配的常见误解的受害者。那些确实 not 产生最短的匹配,但是从 current 光标位置到非贪婪之后表达式的 下一次出现 的匹配(子)表达式。

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

    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)。表达式的下一部分 ([\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
    

    【讨论】:

    • 谢谢。这与我的“工作”示例非常相似......有没有办法有条件地匹配模式中的模式?
    【解决方案3】:

    【讨论】:

    • 我试过退出?并退出{1},但都不起作用。你能指定我应该使用的正确模式字符串吗?谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-08
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    相关资源
    最近更新 更多