Regex 在使用多个可能的匹配目标时,如何计算perl中的正则表达式匹配|&引用;?

Regex 在使用多个可能的匹配目标时,如何计算perl中的正则表达式匹配|&引用;?,regex,perl,alternation,Regex,Perl,Alternation,我有一个(非常)长的数字字符串列表,我需要计算某些值的出现次数,以决定是否拉字符串关联的线。基本上,文件的格式如下: ,4,8,9,11,12, ,5,6,7,9,11, etc. ,(4|9|11)(?=,) 其中字符串的长度范围为1-100个值,值的范围为1-100,字符串中的值始终按从最小到最大的顺序排列 例如,我试图找到三个值中至少有两个值为4、9和11的所有行,下面是我编写的测试代码,用于测试我的正则表达式: my $string = ",4,8,9,11,12,"; my $t

我有一个(非常)长的数字字符串列表,我需要计算某些值的出现次数,以决定是否拉字符串关联的线。基本上,文件的格式如下:

,4,8,9,11,12,
,5,6,7,9,11,
etc.
,(4|9|11)(?=,)
其中字符串的长度范围为1-100个值,值的范围为1-100,字符串中的值始终按从最小到最大的顺序排列

例如,我试图找到三个值中至少有两个值为4、9和11的所有行,下面是我编写的测试代码,用于测试我的正则表达式:

my $string = ",4,8,9,11,12,";

my $test = ",4,|,9,|,11,";

my @c = $string =~ m/$test/g;
my $count = @c;

print "count: $count\n";
print "\@c:, join(" ", @c), "\n";
运行此操作时的输出为:

count: 2
@c:,4, ,9,
当我期望计数为
3
并且@c为
时,4,9,11,

我意识到这是因为9和11共用一个逗号,但我想知道是否有人知道如何避开这个问题。我不能从匹配中删除最后一个逗号,因为如果我试图在一个字符串中匹配
,4
,例如,有
,41,
,它将错误地计算
,41,

我想我可以这样做:

my $test = "4|9|11";
$string =~ s/,/ /;
my @c = $string =~ m/\b($test)\b/g
这是可行的,但在匹配计数之前添加了另一个步骤。有没有办法在保持原始字符串不变的情况下执行匹配

我还试图避免逐个循环遍历我的比赛目标,并对单个比赛计数求和,因为我试图最大限度地提高效率。我正在处理一些非常庞大的值列表,这些值需要数百万次排列,而我目前使用循环编写脚本的方式需要几天才能完成。我希望通过正则表达式匹配它会更快


谢谢

问题是尾随的
,9,
匹配中被消耗,因此当它开始寻找下一个匹配时,它从
11,12,
开始。在
11,
之前没有前导的
,所以它不能匹配。我建议使用如下前瞻:

,4,8,9,11,12,
,5,6,7,9,11,
etc.
,(4|9|11)(?=,)
这样,尾随的
将不会作为匹配的一部分使用

例如:

my $string = ",4,8,9,11,12,";

my $test = ",(4|9|11)(?=,)";

my @c = $string =~ m/$test/g;
my $count = @c;
print "count: $count\n";
print "\@c:", join(" ", @c), "\n";
产出:

count: 3
@c:4 9 11

问题是尾随的
,9,
匹配中使用,因此当它开始寻找下一个匹配时,它从
11,12,
开始。在
11,
之前没有前导的
,所以它不能匹配。我建议使用如下前瞻:

,4,8,9,11,12,
,5,6,7,9,11,
etc.
,(4|9|11)(?=,)
这样,尾随的
将不会作为匹配的一部分使用

例如:

my $string = ",4,8,9,11,12,";

my $test = ",(4|9|11)(?=,)";

my @c = $string =~ m/$test/g;
my $count = @c;
print "count: $count\n";
print "\@c:", join(" ", @c), "\n";
产出:

count: 3
@c:4 9 11

我将使用以下内容而不是正则表达式:

#!/usr/bin/perl

use strict;
use warnings;

my @values = qw(4 9 11);

while (<DATA>) {
    my %hash = map { $_ => 1 } split /,/;

    my $count = 0;
    foreach my $value (@values) {
        $count++ if exists $hash{$value};
    }

    print if $count >= 2;
}

__DATA__
,4,8,9,11,12,
,5,6,7,9,11,
,1,2,3,4,5,

我将使用以下内容而不是正则表达式:

#!/usr/bin/perl

use strict;
use warnings;

my @values = qw(4 9 11);

while (<DATA>) {
    my %hash = map { $_ => 1 } split /,/;

    my $count = 0;
    foreach my $value (@values) {
        $count++ if exists $hash{$value};
    }

    print if $count >= 2;
}

__DATA__
,4,8,9,11,12,
,5,6,7,9,11,
,1,2,3,4,5,

这一项也适用于您,因为您在正则表达式匹配过程中有重叠:

my $str = ',4,8,9,11,12,11,';
my @arr = $str =~ /(?=,(4|9|11),)/g;

这一项也适用于您,因为您在正则表达式匹配过程中有重叠:

my $str = ',4,8,9,11,12,11,';
my @arr = $str =~ /(?=,(4|9|11),)/g;

忽略逗号。这正是您想要的:

printf "count: %d\n", scalar( () = $string =~ /\b(?:4|9|11)\b/g );
空列表的列表赋值
()=…
发生在标量上下文中,由
scalar()
提供,当它返回右侧列表中的元素数时。
(?:…)
只是为了避免创建捕获组,这样可以提高性能

编辑:

好的,OP要求性能,所以我做了一些基准测试,结果是一个简单的

++$count while ($string =~ /\b(?:4|9|11)\b/g);

比我上面的列表分配技巧(在我古老的笔记本电脑上大约30%的加速)和p.s.w.g使用前瞻模式的答案(大约20%的加速,所以他的解决方案实际上可能没有我的第一个解决方案那么新奇,但比我的第一个解决方案快)。

只需忽略逗号即可。这正是您想要的:

printf "count: %d\n", scalar( () = $string =~ /\b(?:4|9|11)\b/g );
空列表的列表赋值
()=…
发生在标量上下文中,由
scalar()
提供,当它返回右侧列表中的元素数时。
(?:…)
只是为了避免创建捕获组,这样可以提高性能

编辑:

好的,OP要求性能,所以我做了一些基准测试,结果是一个简单的

++$count while ($string =~ /\b(?:4|9|11)\b/g);

比我上面的列表作业技巧(在我古老的笔记本电脑上的加速率约为30%)和p.s.w.g使用前瞻模式的答案(加速率约为20%,因此他的解决方案实际上可能没有我的第一个解决方案那么新奇,但比我的第一个解决方案快)。

谢谢。这个解决方案非常有效,正是我想要的。谢谢。这个解决方案工作得很好,正是我想要的。感谢您提供有关
(?:…)
的提示。我所能做的任何改进脚本性能的事情都会有所帮助,但我不知道这一点。@user3091283:我已经编辑了我的答案,以获得显著的性能改进。感谢您提供有关
(?:…)
的提示。我所能做的任何改进脚本性能的事情都会有所帮助,但我不知道这一点。@user3091283:我已经编辑了我的答案,以获得显著的性能改进。