组的可选令牌似乎阻止捕获?

时间:2018-12-19 18:35:17

标签: regex perl

我想捕获X {}和Y {}括号之间的文本:

echo "example ,X{whateverX},...,Y{whateverY} the end" | \
  perl -ne 'print "$2 $4 \n" if /.*(,X\{(.*?)\}).*(,Y\{(.*?)\})/;' 
whateverX whateverY

现在,我想将X和/或Y的存在设置为可选,但是一旦添加可选修饰符,它就会停止匹配/捕获:

echo "example ,X{whateverX},...,Y{whateverY} the end" | \
      perl -ne 'print "$2 $4 \n" if /.*(,X\{(.*?)\})?.*(,Y\{(.*?)\})?/;' 
<nothing printed>

注意:我在上方添加了?每个X / Y组的和处的修饰符,如下所示(最后一个字符):

.\*(,X\\{(.\*?)\\})**?**

.\*(,Y\\{(.\*?)\\})**?**

例如,这里我只有Y作为可选项,只有X被匹配:

echo "example ,X{whateverX},...,Y{whateverY} the end" | \
      perl -ne 'print "$2 $4 \n" if /.*(,X\{(.*?)\}).*(,Y\{(.*?)\})?/;'
whateverX

我希望这三个都产生“ whateverX whatY”,但只有第一个会产生...

我想念什么?为什么将capture-group设置为可选会破坏我的匹配?

3 个答案:

答案 0 :(得分:1)

您应该使自己想起正则表达式的基本方面:默认情况下,只要整个表达式都可以匹配,它们就是贪婪的。

您的示例

/.*(,X\{(.*?)\})?.*(,Y\{(.*?)\})?/

仅具有可选元素,因此它将始终匹配-如果没有其他内容,则为空字符串。

问题是,RE将在尽可能早的位置并且在最大可能的范围内保持贪婪(同时仍然能够匹配表达式的其余部分)。因此,第一个.*将消耗您字符串中的所有内容,而其他子表达式则默认为匹配空字符串(通过?*)。

很难使X {}和Y {}成为可选项,同时仍然希望它们出现。如果将它们设置为可选,则如果可以,则正则表达式引擎最终将永远不会使用它们。

我建议使用在(?:...|...)内部(随后根据所使用的分支为变量分配值)或在分支重置{{1}中存在的X {}和Y {}交替组合的子表达式}(为使用(?|...|...)而编写为正确的代码):

/x

将输出:

use strict;
use warnings;

foreach my $data (<DATA>) {
    chomp $data;

    if ($data =~ /
                     (?|
                         .*?                  # both X and Y present
                         ,X \{ ([^{}]*) \}
                         .*?
                         ,Y \{ ([^{}]*) \}
                     |
                         .*?                  # only X present
                         ,X \{ ([^{}]*) \}
                         .*
                         ()
                     |
                         .*?                  # only Y present
                         ()
                         ,Y \{ ([^{}]*) \}
                     |                
                         () ()                # neither X nor Y present
                     )
                 /x) {

        print "$1, $2\n";
    }
}

exit 0;

__DATA__
example ,X{whateverX},...,Y{whateverY} the end
example2 ,X{whateverX2},random data to the end
example3 with data before ,Y{whateverY3} the end
example4 with just data and no separators

请注意,前导whateverX, whateverY whateverX2, , whateverY3 , 是必需的,否则.*?最终将在每种情况下都匹配。

答案 1 :(得分:0)

特别是因为您的第二个组是可选的,因此您需要确保中间的舍弃匹配第二个块的开头与中间的。*不匹配:

echo "example ,X{whateverX},...,Y{whateverY} the end" | \
perl -ne 'print "$2 $4 \n" if /.*(,X\{(.*?)\})(?:(?!,Y).)*(,Y\{(.*?)\})?/;'
whateverX whateverY 

其中重要的一点是:

(?:(?!,Y).)*

(?:)确保这不是捕获组

(?!,Y)确保此块不包含字符串,Y

如果要更精确,也可以使用(?!,Y {)。

答案 2 :(得分:0)

另一种可以说简单得多的方法:让引擎使用global修饰符每行完成多个匹配。这样,您的模式就变成了琐碎的交替,而没有所有.*?的恶作剧:

/X\{(?<X>.*?)\}|Y\{(?<Y>.*?)\}/g

regex 101 demo