Perl正则表达式 - 如何使其变得更少贪婪?

6
我如何计算以下字符串中空“字段”的数量? 空字段由-||-| |- 表示。 我编写的正则表达式似乎有效,除非有连续的空字段? 我该如何使它变得不那么贪婪?
my $string = 'P|CHNA|string-string|-|-|25.75|-|2562000|-0.06';
my $count = () = ($string=~/(?:^-\||\|-$|\|-\|)/g);   
printf("$count\n");

上面的代码输出2而不是我想要的3。
3个回答

7

对于这个问题,我会避免使用正则表达式的方法,而是视其为一个列表进行处理:

my $count = grep { /^-$/ } split /\|/, $string;

我同意。不过我正在寻找一个正则表达式的解决方案。 - Jean

3
实际上,这个问题与贪婪/懒惰无关(这只适用于重复运算符,如*+)。
问题在于紧挨着的两个空字段:|-|-|。第一个被匹配了,但第二个失败了,因为开头的|已经被消耗掉了,但由于您在规则^-|中有行首标记,它不匹配那个。
我认为更简单的方法是根据|拆分输入,然后查找仅由-组成的任何字段:
my $count = 0;
foreach (split(/\|/,$string)) { if( /^-$/ ) { $count++; } }

实际上,使用正则表达式无法稳健地实现这一点,因为Perl不支持可变长度的向后查找(至少在我所知道的范围内)。其中一种“欺骗”的方法是在开头和结尾附加一个|,然后您可以成功地使用lookbehind / lookahead断言:

$string = "|$string|";
my $count = () = $string=~/(?<=\|)-(?=\|)/g;
(ikegama在下面提供了一种替代解决方案,它使用非变量回溯断言而不修改字符串,所以当我说“没有办法用正则表达式实现这个”的时候是错误的。向ikegama致敬。虽然如此,我仍然认为在这个问题上分割|是最好的方法。)

感谢你指出这一点,@ikegami;\K 元字符对我来说是新的。我现在很少使用 PCRE。如果所有语言都能统一正则表达式语法,那真是太好了!虽然这种情况永远不会发生。 - Ethan Brown
也许一些这样的东西已经被添加到Perl中了;在过去的几年里,我没有跟上Perl的步伐。但.NET有平衡组、交替结构和最后一组替换。它还有命名子表达式,但我不会预见到需要使用它。http://msdn.microsoft.com/en-us/library/az24scfc.aspx - Ethan Brown
自5.10版本以来,它具有用于平衡组的递归(看起来比平衡组更简单、更灵活的解决方案)。自5.8版本以来,它具有(?(condition)yes-pattern|no-pattern)。自5.10版本以来,它具有命名子表达式。不知道您所说的“last group substitution”是什么意思。 - ikegami
我认为贪婪匹配意味着消耗匹配的模式。 - Jean
贪婪匹配(也称最大匹配)意味着重复操作符(*+?,编号匹配)将尽可能匹配。为了使这有任何意义,重复是必要的。 - Ethan Brown
显示剩余3条评论

2
诀窍在于使用环视。有人的第一次尝试可能是以下内容:
my $count = () = $string =~ /
   (?<\|)  # Preceded by "|"
   (-)
   (?=\|)  # Followed by "|"
/xg;

但是这样不行。以上方法存在的问题在于它无法检测第一个或最后一个字段是否为空。有两种方法可以解决:

my $count = () = "|$string|" =~ /
   (?<\|)  # Preceded by "|"
   (-)
   (?=\|)  # Followed by "|"
/xg;

或者

my $count = () = $string =~ /
   (?<![^|])  # Not preceded by a char other than "|"
   (-)
   (?![^|])   # Not followed by a char other than "|"
/xg;

修复了。 (缺少 /g,使用 =~ 而不是 =) - ikegami
不行。这对于字符串开头或结尾的空字段将失败([^|]仍然必须匹配一些内容,但不包括行首/行尾标记)。 - Ethan Brown
@EthanBrown [^|] 匹配失败意味着 (?<![^|]) 成功匹配,这正是我们想要的。 - hobbs
@Ethan Brown,你错了。在指责别人的代码有问题之前,请先测试一下。 - ikegami
1
@Ethan Brown,是的,分割肯定更易读,但无论如何学会如何做也是好的。 - ikegami
显示剩余2条评论

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