如何使这个Perl正则表达式不那么贪婪(或者至少对\Q\E使用否定)

km0tfn4u  于 2023-03-09  发布在  Perl
关注(0)|答案(3)|浏览(130)

我正在尝试将以下行解析为3个变量

BS<syntax interpolate='#'> #greeting</syntax>AS

我需要捕获:$1 = BS$2 = interpolate='#'$3 = AS
问题是我的正则表达式$2'interpolate='#'>#greeting</syntax'。显然它在第二个>上匹配,但我的任何尝试都无法说服它在第一个上匹配。字符<>syntax都被称为%syntax散列中的变量,我的正则表达式是

$line =~ /(\w+)$syntax{'start'}\Q$syntax{'syntax'}\E\s+(.+?)\Q$syntax{'end'}\E(\w+)/i

我试过用([^\Q$syntax{'end'}\E]+)作为第二个括号,甚至是([^\>]+),在这些情况下,它甚至不匹配。奇怪的是,(.+)(.+?)之间似乎没有任何区别。有人能解决这个问题吗?

2lpgd968

2lpgd9681#

为了使.+?在所示字符串中的第一个>处停止,模式.+?>\w+中的\w+需要匹配#greeting</syntax>AS--其中有许多字符\w不匹配(空格、#</)。
一种解决方法是列出所有这些,[\s#<\/\w]+(而不是\w+)。另一种方法是使用更宽松的模式而不是\w。我不知道什么更适合您的问题...
<(等)从散列被内插时,这都成立;此处不需要\Q-\E
但是,如果确实需要捕获AS,那么我们需要多一点。

if ( /(\w+)<syntax \s+ (.+?) > \s+ [\s#\/<\w]+ > (\w+)/ix )

if ( /(\w+)<syntax \s+ (.+?) > \s+ [^>]+ > (\w+)/ix )

是合适的(为了可读性,我放弃了使用%syntax散列)。

hl0ma9xz

hl0ma9xz2#

(?:(?!STRING).)*STRING的关系就像[^CHAR]CHAR的关系一样。

my ( $B, $E, $S ) = map quotemeta, @syntax{qw( start end syntax )};

m{
   (\w+)
   $B $S \s+ ( (?: (?!$E). )+ ) $E
   (?: (?! $B | $E ). )*
   $B /$S $E
   (\w+)
}xsi

我对抽象语法元素的好处表示怀疑。如果你硬编码它们,它就变成了:

m{
   (\w+)
   <syntax \s+ ( [^>]+ ) >
   [^<>]*
   </syntax>
   (\w+)
}xsi

注意,我避免了非贪婪修饰符是不安全的,使用它是不安全的,它并不能阻止你得到比你期望的更多的匹配。
使用正则表达式做这些是非常脆弱的。一个合适的解析器会一次又一次地为自己付出代价。而且你也不需要我们的帮助来编写复杂的正则表达式模式。

hmae6n7t

hmae6n7t3#

这里我有几样东西要推荐。
首先,让散列中的子模式决定它们需要转义什么。我看不到散列,但我怀疑您无意中转义了应该是正则表达式字符的正则表达式字符。
第二,当你插入其他模式时,不要使用编号捕获。如果它们本身有捕获,你会丢掉你的编号。相反,使用命名捕获:

use v5.10;

my %syntax = (
    start  => qr/ < syntax \s+  /x,
    syntax => qr/ (?<interpolate>interpolate = '.+?') > /x,
    end    => qr| < / syntax > |x,
    );

my $string = "BS<syntax interpolate='#'> #greeting</syntax>AS";

$string =~ /
    (?<pre>\w+)
    $syntax{'start'}
    $syntax{'syntax'}
    \s+
    (?<middle>.+?)
    $syntax{'end'}
    (?<post>\w+)
    /x;

use Data::Dumper;
say Dumper( \%+ );

下面是输出,根据需要从哈希中获取值:

$VAR1 = {
          'interpolate' => 'interpolate=\'#\'',
          'middle' => '#greeting',
          'post' => 'AS',
          'pre' => 'BS'
        };

但是,如果您有这些简单的XML/HTML外观片段,DOM可以挑选出您需要的部分:

use v5.10;
use Mojo::DOM;

my $string = "BS<syntax interpolate='#'> #greeting</syntax>AS";
my $dom = Mojo::DOM->new($string);

my $start = $dom->root->descendant_nodes->[0];
say "START: $start";

my $attr = 'interpolate';
my $stuff = $dom->at('syntax')->attr($attr);
say "ATTR: $attr $stuff";

my $middle = $dom->at('syntax')->text;
say "MIDDLE: $middle";

my $start = $dom->root->descendant_nodes->[-1];
say "END: $start";

以下是您需要的所有内容,但您可能需要删除一些空白:

START: BS
ATTR: interpolate #
MIDDLE:  #greeting
END: AS

相关问题