Замените новые строки тегами BR, но только внутри тегов PRE

На складе PHP5, что является хорошим выражением preg_replace для создания этого преобразования:

замените символы новой строки на <br /> , но только внутри <pre> блоков

(Не стесняйтесь делать упрощающие предположения и игнорируйте угловые случаи. Например, мы можем предположить, что теги будут одной строкой, а не такими патологическими вещами, как)

Текст ввода:

 <div><pre class='some class'>1 2 3 </pre> <pre>line 1 line 2 line 3 </pre> </div> 

Вывод:

 <div><pre>1<br />2<br />3<br /></pre> <pre>line 1<br />line 2<br />line 3<br /></pre> </div> 

(Мотивационный контекст: попытка закрыть ошибку 20760 в wikimedia SyntaxHighlight_GeSHI расширении, а также найти мои навыки PHP (в основном я питон) не до табака).

Я открыт для других решений, помимо регулярного выражения, но предпочтительным является небольшое (например, создание html-синтаксического анализа является излишним).

Что-то вроде этого?

 <?php $content = "<div><pre class='some class'>1 2 3 </pre> <pre>line 1 line 2 line 3 </pre> </div> "; function getInnerHTML($Node) { $Body = $Node->ownerDocument->documentElement->firstChild->firstChild; $Document = new DOMDocument(); $Document->appendChild($Document->importNode($Body,true)); return $Document->saveHTML(); } $dom = new DOMDocument(); $dom->loadHTML( $content ); $preElements = $dom->getElementsByTagName('pre'); if ( count( $preElements ) ) { foreach ( $preElements as $pre ) { $value = preg_replace( '/\n|\r\n/', '<br/>', $pre->nodeValue ); $pre->nodeValue = $value; } echo html_entity_decode( getInnerHTML( $dom->documentElement ) ); } 

Основываясь на чем-то, о чем говорил SilentGhost (который почему-то не появляется здесь):

 <?php $str = "<div><pre class='some class' >1 2 3 < / pre> <pre>line 1 line 2 line 3 </pre> </div>"; $out = "<div><pre class='some class' >1<br />2<br />3<br />< / pre> <pre>line 1<br />line 2<br />line 3<br /></pre> </div>"; function protect_newlines($str) { // \n -> <br />, but only if it's in a pre block // protects newlines from Parser::doBlockLevels() /* split on <pre ... /pre>, basically. probably good enough */ $str = " ".$str; // guarantee split will be in even positions //$parts = preg_split('/(<pre .* pre>)/Umsxu',$str,-1,PREG_SPLIT_DELIM_CAPTURE); $parts = preg_split("/(< \s* pre .* \/ \s* pre \s* >)/Umsxu",$str,-1,PREG_SPLIT_DELIM_CAPTURE); foreach ($parts as $idx=>$part) { if ($idx % 2) { $parts[$idx] = preg_replace("/\n/", "<br />", $part); } } $str = implode('',$parts); /* chop off the first space, that we had added */ return substr($str,1); } assert(protect_newlines($str) === $out); ?>