Perl 非贪婪匹配——是“?”字符使用正确吗?

Perl Non-greedy Matching -- Is the "?" character used correctly?

我正在尝试匹配参数声明行的参数名称,如下所示:

parameter BWIDTH = 32;

使用的 Perl 正则表达式是:

$line =~ /(\w+)\s*=/

其中参数名称 BWIDTH 被捕获到 </code>。我遇到的大多数参数都是这样声明的,即名称前面有等号 <code>"=",这就是正则表达式设计为其中包含 "=" (/(\w+)\s*=/) 的原因。

但是也有声明参数的特殊情况:

parameter reg [31:0] PORT_WIDTH [BWIDTH-1:0] = 32;

在这种情况下,我要捕获的参数名称是 PORT_WIDTH。修改正则表达式以匹配此实例不会成功捕获 PORT_WIDTH,尽管它确实可以捕获 BWIDTH

$line =~ /(\w+)(\s*\[.*?\])*\s*=/

其中(\s*\[.*?\])*匹配reg [31:0] PORT_WIDTH [BWIDTH-1:0],这是贪心匹配。

我很困惑为什么元字符 ? 不停止贪婪匹配?我该如何修改正则表达式?

.*? 替换为 [^][]* 以匹配 ][ 以外的 0+ 个字符:

/(\w+)(\s*\[[^][]*])*\s*=/
            ^^^^^^

如果您不使用该值,您也可以将第二个捕获组变为非捕获组。

图案详情:

  • (\w+) - 第 1 组:一个或多个单词字符
  • (\s*\[[^][]*])* - 捕获组(在 ( 之后添加 ?: 以使其成为非捕获组)零次或多次出现:
    • \s* - 0+ 个空格
    • \[ - 文字 [
    • [^][]* - 否定字符 class 匹配 ][
    • 以外的零个或多个字符
    • ] - 文字 ]
  • \s* - 零个或多个空格
  • = - 等号。

贪婪与非贪婪会影响匹配结束的位置,但它仍然会尽可能早地开始。基本上,贪婪匹配是最左边最长的可能匹配,而非贪婪匹配是最左边最短的匹配。但是非贪心还是最左的,不是最右的。

为了得到你想要的,我会使用更明确的描述来描述我想要匹配的内容:/(\w+)(\s*\[[^]]*\])?\s*=/ 在英语中,这是一个单词 (\w+),可以选择后跟一些正方形的文本括号 ((\s*\[[^]]*\])?),然后是可选的空格和等号。请注意,我使用了否定字符 class ([^]]) 而不是括号内的非贪婪匹配 - IMO,否定字符 classes 通常是比非-更好的选择贪心匹配。

使用此正则表达式的结果:

$ perl -E '$x = q(parameter reg [31:0] PORT_WIDTH [BWIDTH-1:0] = 32;); $x =~ /(\w+)(:?\s*\[[^]]*\])?\s*=/; say ;'
PORT_WIDTH
$ perl -E '$x = q(parameter BWIDTH = 32;); $x =~ /:?(\w+)(\s*\[[^]]*\])?\s*=/; say ;'
BWIDTH

您有可用的信息,但您选择不使用。您知道要解析的每个语句的基本结构。这些语句有强制性和可选部分。因此,将您拥有的信息放入比赛中。例如:

#!/usr/bin/env perl

use strict;
use warnings;

my $stuff_in_square_brackets = qr{ \[ [^\]]+ \] }x;

my $re = qr{
    ^
    parameter \s+
    (?: reg \s+)?
    (?: $stuff_in_square_brackets \s+)?
    (\w+) \s+
    (?: $stuff_in_square_brackets \s+)?
    = \s+
    (\w+) ;
    $
}x;

while (my $line = <DATA>) {
    if (my($p, $v) = ($line =~ $re)) {
        print "'$p' = '$v'\n";
    }
}

__DATA__
parameter BWIDTH = 32;
parameter reg [31:0] PORT_WIDTH [BWIDTH-1:0] = 32;

输出:

'BWIDTH' = '32'
'PORT_WIDTH' = '32'