【问题标题】:Is possible combine multiple subsequent capture-group matchning regexes into one?是否可以将多个后续的捕获组匹配正则表达式组合成一个?
【发布时间】:2014-05-03 01:45:54
【问题描述】:

看到this question,开始更深入地思考捕获组。

例如有下一个(示例)输入:

text (aaa (text) ccc) 
text ( aaa (text) ccc )
text ( ' aaa (text) ccc ' ) 
text ( " aaa (text) ccc " )
text (aaa ( ' text ' ) ccc) 
text ( aaa ( ' text ' ) ccc )
text ( ' aaa ( " text " ) ccc ' ) 
text ( " aaa ( ' text ' ) ccc " )

想要捕获anyhing什么代替aaatext(在中间)和ccc,所以想要的结果:

=aaa= =text= =ccc=
=aaa= =text= =ccc=
=aaa= =text= =ccc=
=aaa= =text= =ccc=
=aaa= =text= =ccc=
=aaa= =text= =ccc=
=aaa= =text= =ccc=
=aaa= =text= =ccc=

我有 3 个正则表达式解决方案:

use strict;
use warnings;

while(<DATA>){
    chomp;
    m/
        .*?     #non greedy anything up to
        text    #the first "text"
        \s*     #optional spaces
        \(      #opening (
        (.*)    #content inside () greedy -> $1
        \)      #closing )
        \s*$
    /x;

    #processing only the captured content with removed outside ()
    #remove outside ' or " and extra spaces
    my $inside = $1;
    $inside =~ m/
                #at the begining of "line"
        ^\s*    #optional spaces
        ["']?   #optional " or '
        \s*     #optional spaces

        (.*?)   #content - non greedy -> $1

                #at the end of "line"
        \s*     #optional spaces before the closing ' "
        ['"]?   #optional closing " or '
        \s*$    #optionalny spaces
    /x;

    $inside = $1;
    $inside =~ m/
        ^(\w+)  #any word at the start -> $1
        \s*     #optional spaces
        \(      #opening (
        \s*     #optional spaces
        ['"]?   #optional ' or "
        \s*     #spaces
        (.*?)   #the content inside ' " -> $2
        \s*     #any spaces
        ['"]?   #optional "'
        \s*     #sp
        \)      #closing )
        \s*     #spaces
        (\w+)$  #word at the end -> $3
    /x;

    print "=$1= =$2= =$3=\n";
}
__DATA__
text (aaa (text) ccc) 
text ( aaa (text) ccc )
text ( ' aaa (text) ccc ' ) 
text ( " aaa (text) ccc " )
text (aaa ( ' text ' ) ccc) 
text ( aaa ( ' text ' ) ccc )
text ( ' aaa ( " text " ) ccc ' ) 
text ( " aaa ( ' text ' ) ccc " ) 

问题:

  1. 是否可以将上述所有 3 个正则表达式合并为一个?
  2. 如果是,这是否普遍可行?那么可以将 ANY 数量的后续匹配正则表达式与捕获组组合成 one 正则表达式吗? (仅表示 m// 与捕获组匹配,而不是后续替换正则表达式)
  3. 如果是,什么时候更希望使用一个正则表达式而不是更多?例如什么时候更快更多的正则表达式,当一个大的时候?

Ps:我知道Text::Ballanced 的存在,但这个问题更多的是关于“正则表达式的可能性”。

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    一般来说,您不能只将正则表达式组合在一起。有时你可以,有时你不能。通常情况下,正则表达式最终会更长。例如,对于您上面的那些,您可能会使用这样的东西:

    ^\w+\s*\(\s*(?:(')|("))?\s*(\w+)\s*\(\s*((?(1)"|'))?\s*(\w+)\s*\4?\s*\)\s*(\w+)\s*(?(1)'|")?\s*\)$
    

    Regex101 demo

    以上内容还确保使用了正确的引号(例如,不能在双引号内使用双引号)。所需的组位于$3$5$6。而且还在。 ideone上的一个例子。

    我只评论一些部分:

    ^\w+         # Beginning + function name
    \s*
    \(
    \s*
    (?:(')|("))? # Capture either single or double quote
    \s*
    (\w+)
    \s*
    \(
    \s*
    ((?(1)"|'))? # If a single quote was captured, now match double, and vice versa. Capture
    \s*
    (\w+)
    \s*
    \4?          # Use the 4th capture from above comment
    \s*
    \)
    \s*
    (\w+)
    \s*
    (?(1)'|")?   # Use what was used in first quoting character
    \s*
    \)$
    

    是否更需要使用一个或多个正则表达式取决于用户。如果他们可以一次性完成并且他们不担心它会被维护,那么肯定会。

    如果他们能把它合二为一,而且还能很好地解释,为什么不呢?

    需要注意的是,正则表达式越长,就越容易出错、灾难性的回溯,并且越难以理解。

    较长的正则表达式也不一定比较小的正则表达式慢。有一些工具可以让事情表现得更快;原子组、所有格量词、否定类等等。

    【讨论】:

    • 很公平 :) 你能举个例子吗,(只有m// 和捕获组),其中不可能只有一个正则表达式并且需要两个后续?
    • @cajwine 这与正则表达式的限制有关。例如,perl 正则表达式不支持可变宽度的lookbehinds。说,this question。对于这种特殊情况,您最好使用多个正则表达式而不是制作一个怪物来做到这一点(我真的认为这是不可能的,特别是如果有更多条件适用于问题)。
    【解决方案2】:

    怎么样:

    while(<DATA>){
        chomp;
        /text \((['" ]*)(\w+)\s*\((['" ]*)(\w+)\3\)\s*(\w+)\1\)/ ;
        say "=$2= =$4= =$5=";
    }
    __DATA__
    text (aaa (text) ccc) 
    text ( aaa (text) ccc )
    text ( ' aaa (text) ccc ' ) 
    text ( " aaa (text) ccc " )
    text (aaa ( ' text ' ) ccc) 
    text ( aaa ( ' text ' ) ccc )
    text ( ' aaa ( " text " ) ccc ' ) 
    text ( " aaa ( ' text ' ) ccc " ) 
    

    输出:

    =aaa= =text= =ccc=
    =aaa= =text= =ccc=
    =aaa= =text= =ccc=
    =aaa= =text= =ccc=
    =aaa= =text= =ccc=
    =aaa= =text= =ccc=
    =aaa= =text= =ccc=
    =aaa= =text= =ccc=
    

    【讨论】:

    • OP,如果您不知道,\1\2\3 被称为 反向引用,并允许您引用一个组用同样的表达方式捕捉到。
    猜你喜欢
    • 2020-01-02
    • 1970-01-01
    • 2018-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多