Perl模式匹配不按预期工作

3

我正在尝试使用正则表达式匹配值,这些值可能是用逗号分隔的。基本上,我想要的是:如果字符串中的任何一个值在第三个位置不以3g或3k开头,则返回true。

我的测试代码如下:

my @a = ('in3g123456,dh3k123456,dhec110101','dhec110101,dhec123456','in3g123456,dh3k123456', 'c3kasdf', 'usdfusdufs3gsdf' );

foreach (@a) {
  print $_;
  say $_ =~ /(?:^|,)\w{2}[^(?:3G|3K)]/i ? " true" : " false";
}

这段代码返回:

这返回

in3g123456,dh3k123456,dhec110101 true
dhec110101,dhec123456 true
in3g123456,dh3k123456 false
c3kasdf false   <- whaaaaaaaat?
usdfusdufs3gsdf true

我不明白为什么第四个选项是错误的。感谢任何帮助。

4个回答

2

[^(?:3G|3K)] 的含义是“除了 (? 等之外的任何字符”。

                      failed
                      v
        c3            kasdf
/(?:^|,)\w{2}[^(?:3G|3K)]/i

使用这个:

/(?:^|,)\w{2}(?!3G|3K)/i

示例: https://regex101.com/r/P2XsgN/1.


或者 (?!3[GK]) - Benjamin W.
没错,但是问题不在于优化正则表达式,最小的更改应该最清楚地显示出错误所在。 - Kirill Bulygin

1

如何看待/\b\w{2}(?!3g|3k)/i

\b匹配单词的开头或结尾的空字符串。在这种情况下,它是(^|,)的简化等效形式。

(?!foo)是一个零宽度的负向先行断言。因此,只要不后跟与foo匹配的子字符串,就可以匹配空字符串。


1
你也可以先分割字符串,而不是用正则表达式解析所有内容。这样更灵活、易于维护和操作。
在处理提取的“值”列表时,你可以匹配任何字符两次,然后使用你的模式,/^..$patt/。模块List::MoreUtils非常有用(而且快速)用于列表操作,它的notall函数是为你的条件量身定制的。
use warnings 'all';
use strict;
use List::MoreUtils qw(notall);

my $file = '...';
open my $fh, '<', $file or die "Can't open $file: $!";

while (<$fh>)
{
    my $res = notall { /^..(?:3k|3g)/ } split /,/;

    print "$_: " . ($res ? 'true' : 'false'), "\n";
}

我假设你正在从文件中读取内容。如果不是,请将while(<$fn>)替换为for(@strings)notall函数在列表中任何一个元素未满足条件时返回true。 split默认使用$_,所以我们只需要模式。这里简单地是,,但该模式可以采用正则表达式来匹配分隔符。例如,/[,\s]+/将根据任意数量的,和/或空格进行拆分。因此,字符串中的,, ,也会被视为分隔符,而不仅仅是,或空格。
当应用于包含您的字符串的数组时,上述代码将打印:
in3g123456,dh3k123456,dhec110101: true
dhec110101,dhec123456: true
in3g123456,dh3k123456: false
c3kasdf: true
usdfusdufs3gsdf: true

@Jim 我更新了这个答案,加入了输出和更多的解释。 - zdim

0
你可以使用 substr 来获取第三和第四个位置的数据,然后将其与 (3g|3k) 进行比较。
substr $_,2,2

#!/usr/bin/perl
use strict;
use warnings;

my @a = ('in3g123456,dh3k123456,dhec110101','dhec110101,dhec123456','in3g123456,dh3k123456', 'c3kasdf', 'usdfusdufs3gsdf' );

foreach (@a) {
  my @inputs = split /,/,$_;
  my $flag = 0;
  foreach (@inputs){
    $flag = 1 unless ((substr $_,2,2) =~ /(3g|3k)/);
  }
  $flag ? print "$_: True\n" : print "$_: False\n";
}

输出:

in3g123456,dh3k123456,dhec110101: True
dhec110101,dhec123456: True
in3g123456,dh3k123456: False
c3kasdf: True
usdfusdufs3gsdf: True

演示


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