2012-01-07 50 views
1

我正在嘗試開發一個PHP腳本,用於替換除具有屬性(例如<div id="1">)以外的段落的HTML字符串中的所有div。我的腳本目前所做的第一件事是使用簡單的str_replace()來替換<div><p>的所有匹配項,並且這留下了任何具有屬性和結束div標籤的div標籤(</div>)。但是,用</p>標籤替換</div>標籤有點問題。使用preg_replace_callback函數替換結束div標記

到目前爲止,我已經開發了被設計成一些</div>標籤轉換成</p>標籤相匹配的開放<p>標籤一個preg_replace_callback功能,卻忽略了其他</div>標籤時,他們正在與屬性結尾的<div>。以下是我正在使用的腳本;

<?php 
$input = "<div>Hello world!</div><div><div id=\"1\">How <div>are you</div> today?</div></div><div>I am fine.</div>"; 
$input2 = str_replace("<div>", "<p>", $input); 
$output = preg_replace_callback("/(<div)|(<\/div>)/", 'replacer', $input2); 

function replacer($matches){ 
    static $count = 0; 
    $counter=count($matches); 
    for($i=0;$i<$counter;$i++){ 
     if($matches[$i]=="<div "){ 
      return "<div "; 
      $count++; 
     } elseif ($matches[$i]=="</div>"){ 
      $count--; 
      if ($count>=0){ 
       return "</div>"; 
      } elseif ($count<0){ 
       return "</p>"; 
       $count++; 
      } 
     } 
    } 
} 
echo $output; 
?> 

劇本基本上把所有的剩餘<div></div>標籤到一個數組,然後遍歷它。計數器變量在遇到<div>標記時會遞增,或者在數組中遇到</div>時遞減。當計數器小於0時,返回</p>標籤,否則返回</div>。 腳本的輸出應該是;

<p>Hello world!</p><p><div id="1">How <p>are you</p> today?</div></p><p>I am fine.</p>" 

而是我得到的輸出是;

<p>Hello world!</p><p><div id="1">How <p>are you</p> today?</p></p><p>I am fine.</p> 

我花了好幾個小時對腳本進行了多次編輯,我可以想到,並且我一直獲得相同的輸出。任何人都可以向我解釋我錯在哪裏或提供替代解決方案嗎?

任何幫助,將不勝感激。

+0

看到這個[SO喜愛題外話笑話頁](http://stackoverflow.com/questions/ 1732348 /正則表達式匹配開放標籤,除了-XHTML-自足標籤)。 (這個問題無處不在,但由於某種原因,實際上並不相關)。讀過笑話;儘管大多仍然不正確。你*可以*爲此目的使用正則表達式。這只是有點費力,需要一個'(?R)'遞歸正則表達式。可行,但不值得每次有人詢問時單獨回答。如果您只是使用現成的解決方案(如[tag:phpquery]或[tag:querypath])(html遍歷前端),則更簡單。 – mario 2012-01-07 19:44:58

回答

1

在mario評論的旁邊,可與phpquery或querypath相媲美,您可以使用PHP DOMDocument類搜索有問題的<div>元素,並用<p>元素替換它們。

基石是DOM(文檔對象模型)和XPath:

$input = "<div>Hello world!</div><div><div id=\"1\">How <div>are you</div> today?</div></div><div>I am fine.</div>"; 

$doc = new DOMDocument(); 
$doc->loadHTML("<div id='body'>{$input}</div>"); 
$root = $doc->getElementById('body'); 
$xp = new DOMXPath($doc); 

$expression = './/div[not(@id)]'; 

while($r = $xp->query($expression, $root) and $r->length) 
    foreach($r as $div) 
    { 
     $new = $doc->createElement('p'); 
     foreach($div->childNodes as $child) 
      $new->appendChild($child->cloneNode(1)); 

     $div->parentNode->replaceChild($new, $div); 
    } 
    ; 

$html = ''; 
foreach($root->childNodes as $child) 
    $html .= rtrim($doc->saveHTML($child)) 
    ; 

echo $html; 

這會給你:

<p>Hello world!</p><p><div id="1">How <p>are you</p> today?</div></p><p>I am fine.</p> 
+0

我已經運行了您建議的代碼,並且它對我顯示的代碼非常有用。我唯一的問題是可能有多個div,每個都有不同的id。此外,沒有辦法預測有多少個div或他們可能擁有哪個id。我試着編輯你建議的代碼來滿足我的需求,但沒有成功。不過,非常感謝你回答我的問題。 – siberiantiger 2012-01-07 20:33:40

+0

@siberiantiger:可以用xpath表達式來控制,這更簡單,我會更新答案。 – hakre 2012-01-07 20:35:26

+0

剛剛測試了新代碼。完美的作品!非常感謝。 – siberiantiger 2012-01-07 20:38:39

1

我帶着多個正則表達式不同的方法:

$text = "<div>Hello world!</div><div><div id=\"1\">How <div>are you</div> today?</div></div><div>I am fine.</div><div>an other <div id=\"2\">small</div>test</div><div>nested<div>divs</div>...</div>"; 
echo "before: " . $text . "\n"; 

do 
{ 
    $count1 = 0; 
    $text = preg_replace("/<div>((?![^<]*?<div).*?)<\/div>/", "<p>$1</p>", $text, -1, $count1); 
    $count2 = 0; 
    $text = preg_replace("/<div ([^>]+)>((?![^<]*?<div).*?)<\/div>/", "<temporarytag $1>$2</temporarytag>", $text, -1, $count); 
} while ($count1 + $count2 > 0); 

$text = preg_replace("/(<[\/]?)temporarytag/", "$1div", $text); 

echo "after: " . $text; 

這會給你:

before: <div>Hello world!</div><div><div id="1">How <div>are you</div> today?</div></div><div>I am fine.</div><div>an other <div id="2">small</div>test</div><div>nested<div>divs</div>...</div> 
    after: <p>Hello world!</p><p><div id="1">How <p>are you</p> today?</div></p><p>I am fine.</p><p>an other <div id="2">small</div>test</p><p>nested<p>divs</p>...</p> 

如果你不需要的片斷中,我學會了一些正則表達式的至少我自己:P

+0

非常感謝。我也學到了關於正則表達式的東西。 – siberiantiger 2012-01-08 10:43:21