在Perl中,如何将模式从当前位置(最后一次替换的位置)替换为一行?
我在一行中完成了所有这些替换:
...
s/\[//;
s/(\/\w\w\w\/)/ getMonth $1 /e;
s/:/ /;
s/\s\+\d\d\d\d\]//;
#NOW: replace all blanks with a plus sign from this position until the end of this line.
答案 0 :(得分:8)
我看到你已经接受了答案。但是,对于手头的任务,使用Apache::ParseLog或Apache::LogRegex更合适:
Apache::LogRegex
- 将Apache日志文件中的一行解析为哈希
在我看来,您正在尝试从头开始编写日志文件分析器,这是按月分组日志文件条目的方法。如果是这种情况,请停止重新发明方形轮。
即使您不想使用外部模块,也可以通过使用split进行分割和征服来简化任务:
#!/usr/bin/perl
use strict; use warnings;
use Carp;
use Regex::PreSuf;
my @months = qw(jan feb mar apr may jun jul aug sep oct nov dec);
my %months = map { $months[$_] => sprintf '%02d', $_ + 1 } 0 .. 11;
my $months_re = presuf( @months );
# wrapped for formatting, does not make any difference
my $str = q{62.174.188.166 - - [01/Mar/2003:00:00:00 +0100] "GET
/puntos/img/ganar.gif HTTP/1.1" 200 1551
"http://www.universia.com/puntos/index.jsp";
"Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt; Hotbar 2.0)"};
chomp($str);
my @parts = split qr{\s\[|\]\s}, $str;
if ( $parts[1] =~ m! / ($months_re) / !ix ) {
$parts[1] = $1;
}
$parts[2] =~ s/\s/+/g;
print join(' ', @parts), "\n";
输出:
62.174.188.166 - - Mar "GET+/puntos/img/ganar.gif+HTTP/1.1"+200+1551+"http://www
.universia.com/puntos/index.jsp";+"Mozilla/4.0+(compatible;+MSIE+5.0;+Windows+98
;+DigExt;+Hotbar+2.0)"
答案 1 :(得分:2)
从您的语言来看,您似乎在想象您的替换序列正在通过字符串向前发展,每次替换都会占用最后一个替换的位置。实际上,每个替换都将应用于整个字符串。
当你说“最后一次替换的位置”时,如果之前的替换没有找到什么会发生什么?
在脚本中,你可以这样做:
if ( s/\s\+\d\d\d\d\]// ) { $' =~ s/ /+/g }
但在可重用代码中应避免使用$',因为它会影响其他正则表达式的性能。在那里,你需要做
if ( s/\s\+\d\d\d\d\]// ) { substr($_, $+[0]) =~ s/ /+/g }
但在任何一种情况下,您都需要确保您希望设置$'或@ +的匹配或替换实际上已成功。
答案 2 :(得分:-3)
从Perl 5.6开始,最后一次匹配结束时的位置存储在@+
数组中。整场比赛结束时的位置为$+[0]
。
您可以使用它将字符串拆分为两部分,并仅在后面部分进行替换:
my $base = " pears apples bananas coconuts ";
$base =~ s/apples/oranges/;
my $firstpart = substr($base, 0, $+[0]);
my $secondpart = substr($base, $+[0]);
$secondpart =~ s/ /\+/g;
print '"' . $firstpart . $secondpart . "\"\n";
将打印:
" pears oranges+bananas+coconuts+"
此方法的一个问题是$+[0]
包含替换之前的位置。所以也许有更好的方法:)