[Perl] Замена группы пустых строк

Oks357

Читаю книгу Фридла "Регулярные выражения", в ней в одном из примеров рассматривается замена группы подряд идущих пустых строк на тег <p>. Используется Perl:
 $text =~ s/^\s*$/<p>/mg;  

Но у меня этот пример работает таким образом, что при числе пустых строк больше либо равном двух, появляется два тега <p><p>.
input:
The first paragraph.



The second paragraph.

output:
The first paragraph.
<p><p>
The second paragraph.

С Perl до этого не встречался, причину такого поведения понять не могу. Если это важно версия Perl последняя (v5.12.1) под линукс.

doublemother

У меня есть подозрение, что что-то поломали в движке регулярок (либо я совсем тупой потому что твой вариант работает нежадно, зато вариант \s*+ не работает вообще, а вариант \s+ работает, как надо. То есть, движок почему-то не хочет жадничать, натыкаясь на newline, хотя по идее должен бы.

Bibi

если не говорить регехпу ключ 's', то он не будет считать конец строки пробельным символом.
этого знания должно хватить, чтобы понять поведение.

у меня пример работает
самое забавное, что в javascript-реализации регэкспов ключа s нет. но, как выяснилось, иногда это не повод отчаиваться. если хочется написать что-то вроде
/ ... .*? ... /s
то можно воспользоваться этим шедевром:
/ ... [^]*? ... /

Barbie29

#!/usr/bin/perl -w
$t=qq~The first paragraph.
The second paragraph.~;
$t =~ s/([\n\cM\cJ])\1+/<p>/gs
print $t;

Barbie29

http://www.lor-ng.org/view-article.php?aid=101
m - разрешает метасимволам ^ и $ привязываться к промежуточным символам \n, имеющимся в тексте. Не влияет на работу метасимволов \А, \Z и \z.
while (<>) {
if (m/^yes$/) {
print "Thank you for being agreeable.\n";
}
}
Приведенный выше пример требует комментария. Прежде всего, бросается в глаза наличие двух групп метасимволов для начала и конца строки. В большинстве случаев они означают одно и то же, так как обычно символы новой строки (то есть \n встречающиеся внутри текстового выражения, не рассматриваются как вложенные строки. Однако если для команды m/.../ или s/.../.../ указан модификатор m, то текстовое выражение будет рассматриваться как многострочный текст, в котором границами строк выступают символы новой строки \n. В случае многострочного текста метасимвол ^ сопоставляется с позицией после любого символа новой строки, а не только с началом текстового выражения. Точно также метасимвол $ - это позиция перед любым символом новой строки, расположенным внутри текстового выражения, а не обяательно конец текстового выражения или же позиция перед концевым символом \n. Однако метасимвол \A - начало текстового выражения, а метасимвол \Z - конец текстового выра-жения или позиция перед концевым символом \n, даже если в текстовом выражении имеются вложенные символы \n и при выполнении операции поиска или йены указан модификатор m. Метасимвол точка (.) соответствует любому символу, кроме символа новой строки \n. Независимо от того, задан ли модификатор m, она не будет сопоставляться ни c внутренними, ни с концевыми символами \n. Единственный способ заставить точку рассматривать \n как обычный символ - использовать модификатор s.
Отсюда понятна разница между метасимволами \Z и \z. Если в качестве текстового выражения используется результат чтения входного потока данных, то с большой вероятностью данное выражение заканчивается символом \n, за исключениeм того варианта, когда программа предусмотрительно "отщипнула" его с помощью функции chop или chomp. Метасимвол \Z игнорирует концевой символ \n если он случайно остался на месте, рассматривая обе ситуации как "конец строки". В отличие от него метасимвол \z оказывается более пунктуальным и рассматривает концевой символ \n как неотъемлемую часть проверяемого текстового выражения, если только пользователь не позаботился об удалении этого символа.
Отдельно следует остановиться на метасимволе \G. Он может указыватьсяв регулярном выражении только в том случае, если выполняется глобальный поиск (то есть если команда m/.../ имеет модификатор g). Метасимвол \G, указанный в шаблоне, соответствует точке, на котброй остановилась предыдущая операция поиска.

rosali

> у меня пример работает
уверен?

[xoft ~]$ perl -e '$text = "xxx \n \n \nyyy"; $text =~ s/^\s*$/<p>/mg; print $text'
xxx
<p>
yyy

но

[xoft ~]$ perl -e '$text = "xxx\n\n\nyyy"; $text =~ s/^\s*$/<p>/mg; print $text'
xxx
<p><p>
yyy
[xoft ~]$ perl -e '$text = "xxx\n\n\n\n\n\nyyy"; $text =~ s/^\s*$/<p>/mg; print $text'
xxx
<p><p>
yyy

флаг s не помогает, он вообще про оператор .
там проблема не в жадности, а в какой-то ошибке (ну или если это задокументировано, то "особенности" ^_^) при реализации этих ^ и $, которые матчатся не на символы, а на места между символами. например та же хрень происходит при использовании \b (который тоже матчится между символами)
 
[xoft ~]$ perl -e '$text = "xxx yyy zzz"; $text =~ s/\b\s*\b/<p>/mg; print $text'
<p>xxx<p><p>yyy<p><p>zzz<p>

rosali

$t =~ s/([\n\cM\cJ])\1+/<p>/gs

ну вообще-то изначально задумывалось чтобы пробелы между \n-ами тоже допускались.

ermsoft

Да, в самом деле странное поведение.
Дело, похоже, в символе $, который может интерпретироваться как позиция *до* переноса строки (предпочтительно но может быть и позицией после, или последним символом строки.
Вот например:

$ perl -le '$text = "abc\n\n\nx"; $text =~ s/c\s*$//m; print "($text)"'
(ab
x)

$ perl -le '$text = "abc\n\n\nx"; $text =~ s/c\s*$x//m; print "($text)"'
(abx)

То есть наличие $ перевешивает жадность стоящего перед ним выражения, и "\n" остается после $, если это возможно.
Что происходит дальше, я до конца не разобрался, потому что не очень понимаю, на каких именно позициях стоит окно подстроки при первой и второй замене... Пытался читать use re qw/debugcolor/, но не осилил.
Кстати, у меня на perl 5.8.9 и 5.10.0 два тега <p><p> появляются при числе строк *строго больше* двух.

Barbie29

ну вообще-то изначально задумывалось чтобы пробелы между \n-ами тоже допускались.
у меня чето тоже не выходит однако =(

Bibi

уверен?
мда, действительно, проверил только для двух \n

NataliaS

$text =~ s/(?<=\n\s*\n)+/<p>/g;
Оставить комментарий
Имя или ник:
Комментарий: