3

如果我有一个带有新行的输入,例如:

[INFO]
xyz
[INFO]

如何使用$锚点拉出 xyz 部分?我尝试了类似的模式/^\[INFO\]$(.*?)$\[INFO\]/ms,但 perl 给了我:

Use of uninitialized value $\ in regexp compilation at scripts\t.pl line 6.

有没有办法关闭插值,使锚按预期工作?

编辑:关键是行尾锚是一个美元符号,但有时可能需要将行尾锚散布在图案中。如果模式是插值的,那么您可能会遇到诸如 uninitialized 之类的问题$\。例如,这里可以接受的解决方案是/^\[INFO\]\s*^(.*?)\s*^\[INFO\]/ms,但这并不能解决第一个问题的症结所在。我已经改变了锚点,^所以没有插值发生,有了这个输入,我可以自由地这样做。但是当我真的想$在我的模式中引用 EOL 时呢?如何让正则表达式编译?

4

5 回答 5

5

问题是学术性的——反正你的正则表达式中不需要$锚。您应该使用\n来匹配换行符,因为$唯一匹配换行符和它之前的字符之间的间隙。

编辑:我想说的是,您永远不需要使用$这种方式。从一行到下一行的任何匹配都必须以某种方式使用行分隔符。考虑你的例子:

/^\[INFO\]$(.*?)$\[INFO\]/ms

如果这确实编译,(.*?)则将首先使用第一个换行符并继续前进,直到它匹配\nxyz,第二个$将成功。但是下一个字符是换行符,并且正则表达式正在寻找[,所以这不起作用。回溯之后,(.*?)会不情愿地再消耗一个字符——第二个换行符——但随后$会失败。

每当您尝试将 EOL 与$更多内容匹配时,您必须匹配的第一个“内容”将是换行符,那么为什么不匹配呢?这就是 Perl 正则表达式编译器试图解释$\为正则表达式中的变量名的原因:在行尾锚后跟一个不是行分隔符的字符是没有意义的。

于 2010-05-20T18:55:33.063 回答
4

当正则表达式变得太棘手时,它们可能是错误的工具。我可能会考虑在这里使用触发器运算符。在它的左边为真之前它是假的,然后在它的右边为真之前保持为真。这样,您可以通过查看各个行来选择开始和结束提取的位置:

my $string = <<'HERE';
[INFO]
xyz
[INFO]
HERE

open my $string_fh, '<', \$string;

while( <$string_fh> )
    {
    next if /\[INFO]/ .. /\[INFO]/;
    chomp;

    print "Extracted <$_>\n";
    }

如果您使用的是 Perl 5.10,则可以使用\R以正则表达式结尾的通用行:

use 5.010;

my $string = <<'HERE';
[INFO]
xyz
[INFO]
HERE

my( $extracted ) = $string =~ /(?:\A|\R)\[INFO]\R(.*?)\R\[INFO]\R/;

print "Extracted <$extracted>\n";

不要挂在线末端锚上。

于 2010-05-21T00:48:22.893 回答
4

根据perlfaq6 中的答案-如何在两个模式之间拉出线,它们本身位于不同的线上?,这是单线的样子:

perl -0777 -ne 'print $1,"\n" while /\[INFO\]\s*(.*?)\s*\[INFO\]/sg' file.txt

-0777开关立即在整个文件中啜饮。

但是,如果您正在使用可让您灵活选择要提取的标签的子例程,则该File::Slurp模块会使事情变得更容易:

use strict;
use warnings;
use File::Slurp qw/slurp/;

sub extract {

    my ( $tag, $fileName ) = @_;
    my $text = slurp $fileName;

    my ($info) = $text =~ /$tag\s*(.*?)\s*$tag/sg;
    return $info;
}

# Usage:
extract ( qr/\[INFO\]/, 'file.txt' );
于 2010-05-20T21:04:35.950 回答
1

也许/x修饰符可以帮助:

m/ ^\[INFO\] $ # Match INFO line
   \n
   ^ (.*?) $ # Collect desired line
   \n 
   ^ \[INFO\] # Match another INFO line
/xms

我还没有测试过,所以你可能不得不调试它。但我认为这将阻止$符号作为变量插值。

于 2010-05-31T23:58:34.900 回答
1

虽然我已经接受了 Alan Moore 的回答(Ryan Thompson 的回答也太糟糕了,我只能接受一个),但我想明确解决方案,因为它有点隐藏在评论和讨论中。下面的 Perl 脚本演示了 Perl 使用 $ 来插入变量,如果任何字符继续美元符号,并且关闭插值将允许 $ 被视为 EOL。

use strict;
use warnings;

my $x = "[INFO]\nxyz\n[INFO]";
if( $x =~ /^\[INFO\]$\n(.*?)$\n\[INFO\]/m ) {
    print "'$1' FOUND\n";
} else {
    print "NO MATCH FOUND\n";
}

if( $x =~ m'^\[INFO\]$\n(.*?)$\n\[INFO\]'m ) {
    print "'$1' FOUND\n";
} else {
    print "NO MATCH FOUND\n";
}

if( $x =~ m/ ^\[INFO\] $ # Match INFO line
\n
^ (.*?) $ # Collect desired line
\n 
^ \[INFO\] # Match another INFO line
/xms ) {
    print "'$1' FOUND\n";
} else {
    print "NO MATCH FOUND\n";
}

该脚本产生以下输出:

Use of uninitialized value $\ in regexp compilation at t.pl line 5.
Use of uninitialized value $\ in regexp compilation at t.pl line 5.
NO MATCH FOUND
'xyz' FOUND
'xyz' FOUND
于 2010-06-02T17:44:34.967 回答