首页 > 解决方案 > 组的可选令牌似乎阻止捕获?

问题描述

我想捕获 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 whateverY”,但只有第一个会......

我错过了什么?为什么将捕获组设为可选会破坏我的匹配?

标签: regexperl

解决方案


您应该提醒自己正则表达式的基本方面:只要整个表达式可以匹配,它们默认是贪婪的。

你的例子

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

只有可选元素,所以它总是匹配 - 一个空字符串,如果没有别的。

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

很难让 X{} 和 Y{} 成为可选,同时仍然希望它们存在;如果您将它们设为可选,那么正则表达式引擎将最终不会使用它们,如果它可以逃脱的话。

我建议使用子表达式,其中 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
, 

请注意,前导.*?'s 是必需的,否则()()在每种情况下最终都会匹配。


推荐阅读