有没有一种方法可以评估Perl正则表达式匹配的次数?

12

我一直在阅读 perldoc perlre,以及正则表达式手册和Stack Overflow上相关的问题,但似乎找不到一个非常有用的表达式:如何知道当前匹配的数量?

有关闭合组匹配的表达式($^N),第三个匹配内容的表达式(\g{3} 如果我正确理解文档的话),还有$'$&$`。但是似乎没有一个变量可以告诉我当前匹配的数量。

这个变量真的不存在吗?如果是这样,是否有任何技术上的原因解释为什么很难实现,或者我只是没有仔细阅读perldoc?

请注意,我对内置变量感兴趣,而不是像使用(${$count++})这样的变通方法。

为了背景说明,我正在尝试构建一个正则表达式,它将仅匹配某些匹配项(例如,匹配所有字符“E”的出现次数,但不匹配数组中的数字3、7和10的出现次数)。当我尝试构建一个更符合习惯用法的答案时,我遇到了这个问题,以回答这个SO问题

我想避免将正则表达式作为字符串进行计算,以实际插入3、7和10。


请注意,我对内置变量感兴趣:如果它不在perldoc perlvar中,它是否存在?我认为perlvar包含所有perl内置变量。 - TLP
1
没有这样的变量。perlvar没有记录所有内置变量 - 例如@ISA不会出现 - 但是所有内容都有文档记录。Perl倾向于没有隐藏功能。您能否举一个比您提供的参考更好的解决问题的示例? - Borodin
@DVK:好的,假设有一个变量 ${^MATCH_COUNT},可以达成你想要的效果,请示范一下如何使用它。 - Borodin
代码断言是“内置的”,避免使用它们只是为了复杂化而复杂化。 - hobbs
你有使用过 Regexp::Debugger 模块吗? - tchrist
显示剩余6条评论
2个回答

6

我完全忽略了在另一个问题中使用它的实际效用或智慧。

我认为@-@+可能会做你想要的事情,因为它们保存了编号匹配的偏移量,但看起来正则表达式引擎已经知道最后一个索引将是什么:

use v5.14;

use Data::Printer;

$_ = 'abc123abc345abc765abc987abc123';

my @matches = m/
    ([0-9]+)
    (?{ 
        print 'Matched \$' . $#+ . " group with $^N\n";
        say p(@+);
    })
    .*?
    ([0-9]+)
    (?{ 
        print 'Matched \$' . $#+ . " group with $^N\n"; 
        say p(@+);
    })  
    /x;

say "Matches: @matches";

这会给出字符串,即使尚未匹配$2,也显示最后一个索引为2。

Matched \$2 group with 123
[
    [0] 6,
    [1] 6,
    [2] undef
]
Matched \$2 group with 345
[
    [0] 12,
    [1] 6,
    [2] 12
]
Matches: 123 345

请注意,第一次运行时$+[2]是未定义的,因此尚未填充。您可能可以对此进行某些操作,但我认为这可能会偏离您问题的本意。如果您非常高级,您可以创建一个绑定标量,其值为@+中最后一个已定义的索引。

5

我稍微尝试了一下。再次声明,我知道这并不是您想要的方式,但我认为以您期望的方式不存在。

我有两个想法。首先,使用保留分隔符模式进行拆分,可以将插入位作为输出列表中的奇数元素。使用拆分的列表,计算出您需要的匹配项,然后将其按照自己的方式重新组合:

use v5.14;

$_ = 'ab1cdef2gh3ij4k5lmn6op7qr8stu9vw10xyz';

my @bits = split /(\d+)/; # separator retention mode

my @skips = qw(3 7 10);
my $s;
while( my( $index, $value ) = each @bits ) {
    # shift indices to match number ( index = 2 n - 1 )
    if( $index % 2 and ! ( ( $index + 1 )/2 ~~ @skips ) ) {
        $s .= '^';
        }
    else {
        $s .= $value;
        }
    }

我会翻译:

ab^cdef^gh3ij^k^lmn^op7qr^stu^vw10xyz

我曾经认为我对split函数的回答非常好,但后来我又想了一下。在替换操作中是否可以使用state关键字呢?看起来是可以的:

use v5.14;
$_ = 'ab1cdef2gh3ij4k5lmn6op7qr8stu9vw10xyz';
my @skips = qw(3 7 10);

s/(\d+)/
    state $n = 0;
    $n++;
    $n ~~ @skips ? $1 : '$'
    /eg;

say;

这给了我:
    ab$cdef$gh3ij$k$lmn$op7qr$stu$vw10xyz

我认为即使存在这个神奇的变量,也不可能比那更简单了。

我有第三个想法,但没有尝试。我想知道state在代码断言中是否有效。它可能有效,但是我必须弄清楚如何使用其中之一来使匹配失败,这实际上意味着它必须跳过可能已经匹配的部分。这似乎非常复杂,这可能就是Borodin在压力你展示伪代码时所要表现的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接