Нужна помощь с preg_replace

Kela

Новичок
Нужна помощь с preg_replace

К примеру, есть такой текст:
PHP:
$msg = '
    <p class="someclass">Привет, <b>Коля</b>! Как жизнь?</p>
    <p>Спасибо,- <b>Хорошо!</b> Как ты?</p>

    <p class="someclass">Привет, Коля! Как жизнь?</p>
    <p>Спасибо,- <b>Хорошо!</b> Как ты?</p>';
(в первом примере слово Коля заключено в тег <b>, во втором варианте - нет)

задача состоит в том, чтобы убрать тэг <b> из абзаца с классом "someclass". Я пишу такой код:

PHP:
$msg = preg_replace('~<p class="someclass">(.*?)<b>(.*?)</b>(.*?)</p>~ius', '<p class="someclass">$1$2$3</p>', $msg);
В первом случае код отрабатывается нормально и тег убирается, однако во втором случае тег <b> убирается из второго абзаца, у которого нет класса "someclass".
После перечитывания мануала в третий раз, я сообразил, что это происходит из-за того, что во втором случае шаблон (.*?) "съедает" все до первого встреченного тега <b>, не останавливаясь на первом закрывающем теге </p>, как я задумывал. Таким образом, тег убирается из второго абзаца.

Собственно вопрос: как заставить регулярное выражение не перешагивать через первый встреченный закрывающий тег </p>? Иными словами, как делать сопоставление регулярного выражения ТОЛЬКО в пределах абзаца?

Для удобства, здесь исходный текст программы (17 строк, 560 байт), здесь она же в работе.
 

Dl

Новичок
Попробовать добавить условие, чтобы до <b> не встречалась последовательность </p>?
 

Kela

Новичок
Я бы попробывал, но не знаю, как слепить все вкучу потом :)

Шаблон (?<!</p>) - перед текущей позицией нет </p>

как сформировать шаблон: "что угодно, кроме </p>"?
 
Может легче разбить задачу на составляющие? preg_match_all'ить все что между <p class="comeclass"></p> и уже там preg_replace'ить <b> ? особенно если нет уверенных знаний регулярок.
 

Kela

Новичок
mishco
Это на крайний случай, не хотелось бы связываться с циклами там, где можно обойтись регуляркой... Но если никто не предложит такой вариант - придется так...
 

Dl

Новичок
Подсказка:
между <p class="someclass"> и <b> можно вставить символьный класс с условием "что угодно, кроме </p>"
 

Kela

Новичок
Dl
Я это понимаю, и словами сказать могу, а в регулярных выражениях описать не могу... :(
 

Kela

Новичок
ОК. Буду размышлять вслух.

[^p] - что угодно, кроме символа p. Пробуем сделать обратную задачу: соответствует </p>.

(</p>) == ([<][/][p][>])

Применим отрицание:

([^<][^/][^p][^>])*? - получим в круглых скобках "четыре символа, вместе которые НЕ составляют </p>", за скобками: "вся эта конструкция может быть ноль или более раз"!

Похоже то, что нам нужно! Исключим этот шаблон из соответствия и получим так: (?:[^<][^/][^p][^>])*?

Итого, окончательный вариант должен звучать так:

PHP:
$msg = preg_replace('~<p class="someclass">((?:[^<][^/][^p][^>])*?)<b>(.*?)</b>(.*?)</p>~ius', '<p class="someclass">$1$2$3</p>', $msg);
Что я могу сказать? - На практике работает! А в теории, никто подводных камней не видит, как в моем первоначальном варианте?

Добавлено:

Кстати, в парсере форума ошибка: если написать > с последующей закрывающей круглой скобкой, то между ними появится пробел!

>)

-~{}~ 15.08.07 15:59:

Ошибку нашел сам. :( Мы опять обламаемся, если перед <b> будет стоять какой-то другой тег, например <i>. :(

У меня сейчас мозги вскипят :) Я плакаль.... Как же сделать отрицание именно </p>?
 
Kela
это не ошибка, это специально. и это не только в этом случае, например в <some event="javascript:foobar();">tag?</sometag>
 

Kela

Новичок
mishco
Ну, ладно.... это оффтоп был... меня больше регульрное выражение интересует... :)
 

Kela

Новичок
Может, я что-то не так понимаю, но символ ^ означает отрицание только в начале символьного класса [^a] (не а). В противном случае он означает начало данных.

Таким образом ^(</p>) не есть отрицание </p>, ИМХО.

Исправьте, если я ошибаюсь...
 

Lews

Новичок
~ius заменить на ~iUs

-~{}~ 16.08.07 00:58:

в самой первой регулярке.

Единственное - никакие ухищрения не помогут с вложенными тэгами <p> - там только циклом/рекурсией.
 

Kela

Новичок
Lews

строка в UTF8,- поэтому и параметр u стоит.

А добавлением U вы всего лишь инвертируете "жадность" квантификаторов. Т.е. * станет "не жадным", а *? - жадным.

Успеха это не добавит, к сожалению...
 

telega-ru

Новичок
Если нужно не именно убрать тег, а просто не отображать его жирным шрифтом, можно сделать это с помощью css:
<style>
.someclass b {
font-weight: normal;
}
</style>
 

Dl

Новичок
Ну, можно использовать preg_replace_callback и внутри проверять на </p><p>
 

Splurov

Новичок
Kela
А если так:
PHP:
$msg = preg_replace('~(<p class="someclass">.*?)<b>(.*?)</b>(.*?</p>)((?:.*?</p>)*)~is', '$1$2$3$4', $msg);
?
пробелы лишние между ">" и ")" :)
 
Сверху