Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > Замена символов между тэгами


Автор: amg 12.7.2007, 13:02
С виду простая задачка:
есть строка
$_ = 'aXbX<a>cXbX</a>eXgXaXbX<a>cXbX</a>eXgX';
нужно заменить X на У, но не везде, а только внутри тэгов.
Т.е. должно получиться
aXbX<a>cYbY</a>eXgXaXbX<a>cYbY</a>eXgX

Понятно, что можно выкусить нужные подстроки, сделать в них замены и вставить обратно, либо как-то так:
Код

s|(?<=<a>)(.+?)(?=</a>)|($a="$1")=~s/X/Y/g;$a|eg;

Но верится, что что так просто формулируемая задачка не решается обыкновенным регулярным выраженим, без выполнения дополнительного кода: что-то подобное уже http://forum.vingrad.ru/topic-148509.html, и тогда нашлось потрясающе элегантное http://forum.vingrad.ru/index.php?showtopic=148509&view=findpost&p=1115071.

Автор: Nab 12.7.2007, 14:43
У меня пока получилось вот такое
Код

s!(<a>[^<]*?|\G[^<]*?)X!$1Y!g;


Но учитывая что \G без предыдущего совпадения соответствует ^, то в подстроке желательно убрать лидирующий текст до первого тега...

Буду думать еще, может и нарисуюю работающий вариант...

Автор: Nab 12.7.2007, 15:13
Сейчас есть решение, не в одну регулярку а в две smile
первой просто позиционирую первую pos для \G:
Код

m!^[^<]*!g;
s!(<a>[^<]*?|\G[^<]*?)X!$1Y!g;

Автор: JAPH 12.7.2007, 15:42
У меня такое, которому далеко до совершенства:
Код
s#\G([^<X]*)X#$1Y#g while /<a>/g;

Автор: JAPH 12.7.2007, 16:20
Уложился в одну регулярку. Насколько элегантно - не знаю.. smile
Код
s#(^.*?<a>.*?|\G(?:[^<]*?|<(?!/a>))*?|\G.*?</a>.*?<a>.*?)X#$1Y#g;


Добавлено @ 16:25
Преимущества - применима к замене не только одного символа.
Недостатки - длинная smile + не понимает вложенных тегов

Автор: amg 13.7.2007, 10:09
NabJAPH, как всегда, на высоте! Спасибо большое. Может настанут, наконец, те светлые времена, когда и я буду понимать, как работает \G

Но я, как всегда, перемудрил с вопросом. Засада в том, что в на самом деле мне нужно заменять не X на Y, а < и > на &lt; и &gt; соответственно.

Автор: JAPH 13.7.2007, 13:42
Так с этого и надо было начинать! (:
Ну, скорее, недомудрили smile
Одной регуляркой без 'e' уже не обойтись, так как возможностей замены две.
Код
s{(^.*?<a>|\G)((?:(?>[^<>]+)|</a>.*?<a>)*)(?:(</a>(?!<a>)(?>.*)$)|([<>]))}
 {$1.$2.($4 ? ($4 eq '<' ? '&lt;' : '&gt;') : $3)}ge;

Немного подумав
Хотя.. smile
Код

my %a = ('<' => '&lt;', '>' => '&gt;');
no warnings 'uninitialized';
s#(^.*?<a>|\G)((?:(?>[^<>]+)|</a>.*?<a>)*)(?:(</a>(?!<a>)(?>.*)$)|([<>]))#$1$2$a{$4}$3#g;

Автор: amg 13.7.2007, 15:06
JAPH, гениально! Твое решение даже легко вполне универсальным сделать. 
Код

my %a = ('<' => '&lt;', '>' => '&gt;');
my $r = join '', keys %a;
my $b = qr(\Q<a>\E); 
my $e = qr(\Q</a>\E);
no warnings 'uninitialized';
s/(^.*?$b|\G)((?:(?>[^$r]+)|$e.*?$b)*)(?:($e(?!$b)(?>.*)$)|([$r]))/$1$2$a{$4}$3/sg;
Мелочь, конечно, но приятно, что при этом можно использовать s/// - мой редактор только для такой конструкции регулярные выражения правильно раскрашивает.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)