2009-11-15 2 views
2

В Perl, как я могу заменить шаблон из текущего положения (положение последней замены) до конца строки?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

Просьба привести пример ввода, в котором вы выполняете операции '' /// '. –

+0

Это строка журнала веб-сервера, я не включил все замены в свой пример кода. 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 (совместимо; MSIE 5.0; Windows 98; DigExt; Hotbar 2.0) " – Lucia

ответ

-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] содержит позицию до замена. Поэтому, возможно, есть лучший способ:

+3

Было бы намного лучше заменить эту последовательность операций' s /// ', которые имеет OP (что, кажется, говорит «замените месяц + год в квадратных скобках тем, что« getMonth »возвращает») с упрощенной операцией, которая является более кратким и позволяет остальным требованиям удовлетвориться. Но это требует сотрудничества от ОП. –

+1

** '$ +' ** ** не ** массив. ** '@ +' ** есть. Я исправил вашу ошибку и связал ее с правильным местоположением в документации. Откат этих фактических исправлений (которые вы можете легко проверить) неверен. http://perldoc.perl.org/perlvar.html#%40%2b –

+0

@Sinan Ünür: Если вы добавите комментарий, я могу отредактировать свой ответ, если соглашусь (сделано здесь) – Andomar

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)"

2

С вашего языка, вы Кажется, вы представляете себе, что ваша последовательность замещений работает вперед через строку, причем каждая замена занимает место, где последний остановился. Фактически, каждая подстановка будет применяться ко всей строке.

Когда вы говорите «положение последней замены», что должно произойти, если предыдущая замена ничего не обнаружила?

В скрипте вы можете просто сделать:

if (s/\s\+\d\d\d\d\]//) { $' =~ s/ /+/g } 

но использование $»следует избегать многократного использования кода, так как это может повлиять на производительность других регулярных выражений. Там, вы должны были бы сделать

if (s/\s\+\d\d\d\d\]//) { substr($_, $+[0]) =~ s/ /+/g } 

, но в любом случае, вы должны убедиться, что матч или замену вы ожидаете иметь множество $»или @ + на самом деле удалось.

+0

«this» - это позиция последней замены, в которой соответствует '\ s \ + \ d \ d \ d \ d'. Если вы знаете лучший способ, чем '$ + [0]', напишите :) – Andomar

+1

@Andomar: извините, не прочитал вопрос достаточно хорошо; полностью заменил мой ответ – ysth

+0

+1 Вероятно, 's /. */+ /' должно быть 's// +/g', но приятно видеть, что замена на substr меняет исходную строку – Andomar

Смежные вопросы