如果我输入了新行,例如:
[INFO]
xyz
[INFO]
如何使用$
锚点拉出xyz部分?我试过像/^\[INFO\]$(.*?)$\[INFO\]/ms
这样的模式,但perl给了我:
Use of uninitialized value $\ in regexp compilation at scripts\t.pl line 6.
有没有办法关闭插值,以便锚点按预期工作?
编辑:关键是行尾锚是一个美元符号,但有时可能需要通过模式散布行尾锚。如果模式是插值,那么您可能会遇到诸如未初始化$\
之类的问题。例如,这里可接受的解决方案是/^\[INFO\]\s*^(.*?)\s*^\[INFO\]/ms
,但这并不能解决第一个问题的症结所在。我已经将锚点更改为^
,因此没有插值,并且通过此输入我可以自由地执行此操作。但是,当我真的想在我的模式中使用$
引用EOL时呢?如何编译正则表达式?
答案 0 :(得分:4)
问题是学术性的 - 无论如何都不需要你的正则表达式中的$
锚。您应该使用\n
来匹配换行符,因为$
仅匹配换行符与其前面的字符之间的差距。
编辑:我想说的是,你从不需要以这种方式使用$
。从一行到下一行的任何匹配都必须使用行分隔符以某种方式。考虑一下你的例子:
/^\[INFO\]$(.*?)$\[INFO\]/ms
如果这确实编译了,(.*?)
将首先消费第一个换行符并继续前进,直到匹配\nxyz
,其中第二个$
将成功。但是下一个字符是换行符,正则表达式正在寻找[
,所以这不起作用。回溯后,(.*?)
会不情愿地再消耗一个字符 - 第二个换行符 - 但$
会失败。
每次尝试将EOL与$
匹配,然后再添加更多内容时,您必须匹配的第一个“内容”就是换行符,那么为什么不匹配呢?这就是为什么Perl正则表达式编译器试图将$\
解释为正则表达式中的变量名称的原因:使用行尾锚点后跟一个不是行分隔符的字符是没有意义的。
答案 1 :(得分:4)
根据perlfaq6 - How can I pull out lines between two patterns that are themselves on different lines?中的答案,这是一个单行的样子:
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' );
答案 2 :(得分: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";
不要挂在最后的锚点上。
答案 3 :(得分:1)
也许/x
修饰符可以提供帮助:
m/ ^\[INFO\] $ # Match INFO line
\n
^ (.*?) $ # Collect desired line
\n
^ \[INFO\] # Match another INFO line
/xms
我没有测试过,所以你可能需要调试它。但我认为这会阻止$
符号作为变量进行插值。
答案 4 :(得分:1)
虽然我已经接受了艾伦摩尔的回答(瑞恩汤普森的答案也会做得太糟糕,我只能接受一个)我想完全清楚解决方案,因为它有点埋没在评论和讨论中。下面的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