【问题标题】:Regex: capturing groups within capture groups正则表达式:在捕获组中捕获组
【发布时间】:2015-07-24 22:51:54
【问题描述】:

简介

(如果您对前奏感到厌烦,可以跳至如果...

这个问题并不是特别针对 VBScript(我只是在这种情况下使用它):我想为一般正则表达式的使用找到一个解决方案(包括编辑器)。

这开始于我想创建Example 4 where 3 capture groups are used to split data across 3 cells in MS Excel 的改编版本。 我需要捕获一个完整的模式,然后在其中捕获 3 个其他模式。但是,在同一个表达式中,我还需要捕获另一种模式,并在其中再次捕获其他 3 种模式(是的,我知道……但在指向 nutjob 手指之前,请完成阅读)。

我首先想到了Named Capturing Groups,然后我意识到我不应该«混合命名和编号的捕获组»,因为它«不推荐,因为组的风格不一致编号»

然后我查看了 VBScript SubMatches«non-capturing» groups 并找到了针对特定案例的可行解决方案:

For Each C In Myrange
    strPattern = "(?:^([0-9]+);([0-9]+);([0-9]+)$|^.*:([0-9]+)\s.*:([0-9]+).*:([a-zA-Z0-9]+)$)"

    If strPattern <> "" Then
        strInput = C.Value

        With regEx
            .Global = True
            .MultiLine = True
            .IgnoreCase = False
            .Pattern = strPattern
        End With

        Set rgxMatches = regEx.Execute(strInput)

        For Each mtx In rgxMatches
            If mtx.SubMatches(0) <> "" Then
                C.Offset(0, 1) = mtx.SubMatches(0)
                C.Offset(0, 2) = mtx.SubMatches(1)
                C.Offset(0, 3) = mtx.SubMatches(2)
            ElseIf mtx.SubMatches(3) <> "" Then
                C.Offset(0, 1) = mtx.SubMatches(3)
                C.Offset(0, 2) = mtx.SubMatches(4)
                C.Offset(0, 3) = mtx.SubMatches(5)
            Else
                C.Offset(0, 1) = "(Not matched)"
            End If
        Next
    End If
Next

Here's a demo in Rubular of the regex。 在这些:

124;12;3
我的 id1:213 我的 id2:232 我的话:ins4yanrgx
:8587459 :18254182540215 :dcpt
0;1;2

它返回带有数字的前 2 个单元格和带有数字或单词的第三个rd。 基本上,我使用了一个具有 2 个“父”模式的非捕获组(“父”= 我想检测其他子模式的广泛模式)。如果 1st 父模式具有匹配的子模式(1st 捕获组),那么我将其值和该模式的剩余捕获组放在 3 个单元格中。如果没有,我检查第 4th 捕获组(属于 2nd 父模式)是否匹配,并将剩余的子模式放在相同的 3 个单元格中。

如果...

而不是这样:

(?:^(\d+);(\d+);(\d+)$|^.*:(\d+)\s.*:(\d+).*:(\w+)$|what(ever))

这样的事情是可能的:

(#:^(\d+);(\d+);(\d+)$)|(#:^.*:(\d+)\s.*:(\d+).*:(\w+)$)|(#:what(ever))

(#: 不是创建一个非捕获组,而是创建一个“父”编号的捕获组。 这样我就可以做类似于Example 4的事情:

C.Offset(0, 1) = regEx.Replace(strInput, "#$1")
C.Offset(0, 2) = regEx.Replace(strInput, "#$2")
C.Offset(0, 3) = regEx.Replace(strInput, "#$3")

它将搜索父模式,直到在子模式中找到匹配项(将返回第一个匹配项,理想情况下,不会搜索剩余的匹配项)。

已经有类似的东西了吗?或者我完全从正则表达式中遗漏了一些允许这样做的东西?

其他可能的变化:

  • 直接引用父子模式,例如:#2$3(在我的示例中相当于$6);
  • 根据需要在其他人中创建尽可能多的捕获组(我想这会更复杂,但也是最有趣的部分),例如:使用像(#:^_(?:(#:(\d+):\w+-(\d))|(#:\w+:(\d+)-(\d+)))_$)|(#:^\w+:\s+(#:(\w+);\d-(\d+))$) 这样的正则表达式(相同的语法)并以像这样的模式获取##$1

    _123:smt-4_ 匹配:123
    _ott:432-10_ 匹配:432
    yant: special;3-45235 匹配:特殊

如果您发现此逻辑中有任何错误或缺陷,请告诉我,我会尽快编辑。

【问题讨论】:

  • 您似乎在试图让事情看起来比实际更困难。您可以在捕获组中使用捕获组,它们被编号或命名,您可以随时访问它们。 IMO,实际上没有必要创建这样的捕获组层次结构。也许.NET Captures 属性 - 获取与捕获组匹配的所有捕获的集合,按最内左最前的顺序 接近您的要求。尽管如此,您仍无法按照描述的方式访问它们。
  • @stribizhev 谢谢,我之前确实看到过那些 .NET 捕获(我写的示例使用 SubMatches 来获取非 cap.group 中的 cap.groups,通过在这些中再次使用 Execute我想我可以无限期地在层次结构中下降)。实际需要是任何人都可以在任何支持正则表达式的 IDE/编辑器中进行搜索和替换,而不是为相同目的编写循环...In this rubular,每个匹配只有 3 是否更有意义结果会显示而不是每次显示 6 个,其中 3 个是空的?
  • 这对 .NET 来说没问题,您可以使用多个命名的捕获组,并且在匹配非空时重写它们。尝试在 .NET 中为交替运算符两侧的相应捕获组使用命名组

标签: regex substring match capture-group


【解决方案1】:

这通常是要捕获大部分相同数据的情况。
唯一的区别在于形式。

有一个称为分支重置的正则表达式构造。
它在大多数 Perl 兼容引擎上提供。不是 Java 也不是 Dot Net。
它主要只是节省正则表达式资源并使其更容易处理匹配。

您提到的替代方案没有任何帮助,它实际上只是使用
更多资源。您仍然需要查看匹配的内容才能了解您的位置。
但是您只需要检查集群中的一个组,就可以知道是哪个组
组是有效的(

下面是使用RegexFormat 6构造的)

这里是分支重置版本:

 # (?|^(\d+);(\d+);(\d+)$|^.*:(\d+)\s.*:(\d+).*:(\w+)$|what(ever)()())

 (?|
      ^ 
      ( \d+ )                       # (1)
      ;
      ( \d+ )                       # (2)
      ;
      ( \d+ )                       # (3)
      $ 
   |  
      ^ .* :
      ( \d+ )                       # (1)
      \s .* :
      ( \d+ )                       # (2)
      .* :
      ( \w+ )                       # (3)
      $ 
   |  
      what
      ( ever )                      # (1)
      ( )                           # (2)
      ( )                           # (3)
 )

这是你的两个正则表达式。请注意,“父”捕获实际上增加了组的数量(这会降低引擎的速度):

 # (?:^(\d+);(\d+);(\d+)$|^.*:(\d+)\s.*:(\d+).*:(\w+)$|what(ever))

 (?:
      ^ 
      ( \d+ )                       # (1)
      ;
      ( \d+ )                       # (2)
      ;
      ( \d+ )                       # (3)
      $ 
   |  
      ^ .* :
      ( \d+ )                       # (4)
      \s .* :
      ( \d+ )                       # (5)
      .* :
      ( \w+ )                       # (6)
      $ 
   |  
      what
      ( ever )                      # (7)
 )

    # (#:^(\d+);(\d+);(\d+)$)|(#:^.*:(\d+)\s.*:(\d+).*:(\w+)$)|(#:what(ever))

    (                             # (1 start)
         \#: ^ 
         ( \d+ )                       # (2)
         ;
         ( \d+ )                       # (3)
         ;
         ( \d+ )                       # (4)
         $ 
    )                             # (1 end)
 |  
    (                             # (5 start)
         \#: ^ .* :
         ( \d+ )                       # (6)
         \s .* :
         ( \d+ )                       # (7)
         .* :
         ( \w+ )                       # (8)
         $ 
    )                             # (5 end)
 |  
    (                             # (9 start)
         \#:what
         ( ever )                      # (10)
    )                             # (9 end)

【讨论】:

  • 哇!为此+1!几乎是一箭双雕。在regular-expressions.info 中,他们实际上解释了“分支重置组”对备选方案进行分组并合并它们的捕获组。尝试过RegexFormat 6,但在我的 Win 7 中执行时它崩溃了。然而,我渴望更多:直接访问他们父母识别的子模式......这就是为什么我使用 (#: 的奇怪语法正则表达式和#$1 用于匹配模式(它实际上在正则表达式语法中不存在......反正我不知道:)。
  • @Armfoot - RegexFormat 6 在我的 32 位和 64 位 Win 7 机器上完美安装和运行。你有什么问题,你安装了什么子版本? 6.02 是当前版本。我认识编写该软件的人,我认为他们会针对合法软件错误免费提供注册密钥。将有关如何重现错误的详细报告发送到 support@regexformat.com,您可能会获得免费密钥。回到主题.. 如果我是正则表达式引擎设计者,我不会缓冲嵌套的捕获组,只需将迭代器的数组/链表维护到源字符串中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多