正则表达式是我作为解决方案的最初想法,尽管很快就会发现DOM解析器更合适......我想在HTML文本字符串中的PRE标记之间将空格转换为
。例如:
<table atrr="zxzx"><tr>
<td>adfa a adfadfaf></td><td><br /> dfa dfa</td>
</tr></table>
<pre class="abc" id="abc">
abc 123
<span class="abc">abc 123</span>
</pre>
<pre>123 123</pre>
into(注意span标记属性中的空格被保留):
<table atrr="zxzx"><tr>
<td>adfa a adfadfaf></td><td><br /> dfa dfa</td>
</tr></table>
<pre class="abc" id="abc">
abc 123
<span class="abc">abc 123</span>
</pre>
<pre>123 123</pre>
结果需要序列化为字符串格式,以便在其他地方使用。
答案 0 :(得分:2)
当您想要插入
实体而没有DOM将&符号转换为&
实体时,这有点棘手,因为实体是节点,空格只是字符数据。以下是如何做到这一点:
$dom = new DOMDocument;
$dom->loadHtml($html);
$xp = new DOMXPath($dom);
foreach ($xp->query('//text()[ancestor::pre]') as $textNode)
{
$remaining = $textNode;
while (($nextSpace = strpos($remaining->wholeText, ' ')) !== FALSE) {
$remaining = $remaining->splitText($nextSpace);
$remaining->nodeValue = substr($remaining->nodeValue, 1);
$remaining->parentNode->insertBefore(
$dom->createEntityReference('nbsp'),
$remaining
);
}
}
获取所有pre元素并使用其nodeValues在这里不起作用,因为nodeValue属性将包含所有子节点的组合 DOMText值,例如:它将包括span子节点的nodeValue。在pre元素上设置nodeValue会删除它们。
因此,我们不是获取前节点,而是获取所有在其轴上某处具有前元素父节点的DOMText节点:
DOMElement pre
DOMText "abc 123" <-- picking this
DOMElement span
DOMText "abc 123" <-- and this one
DOMElement
DOMText "123 123" <-- and this one
然后我们遍历每个DOMText节点并将它们拆分为每个空间的单独DOMText节点。我们删除空格并在拆分节点之前插入实体节点,因此最终会得到一个像
的树DOMElement pre
DOMText "abc"
DOMEntity nbsp
DOMText "123"
DOMElement span
DOMText "abc"
DOMEntity nbsp
DOMText "123"
DOMElement
DOMText "123"
DOMEntity nbsp
DOMText "123"
因为我们只使用了DOMText节点,所以任何DOMElements都保持不变,因此它将保留pre元素中的span元素。
警告:
您的代码段无效,因为它没有根元素。当使用loadHTML时,libxml会将任何缺少的结构添加到DOM中,这意味着您将获得包含DOCTYPE,html和body标签的代码段。
如果您想要恢复原始代码段,则必须getElementsByTagName
正文节点并获取所有子代码以获取innerHTML
。不幸的是,there is no innerHTML function or property in PHP's DOM implementation,所以我们必须手动执行此操作:
$innerHtml = '';
foreach ($dom->getElementsByTagName('body')->item(0)->childNodes as $child) {
$tmp_doc = new DOMDocument();
$tmp_doc->appendChild($tmp_doc->importNode($child,true));
$innerHtml .= $tmp_doc->saveHTML();
}
echo $innerHtml;
另见
答案 1 :(得分:1)
我看到我之前的回答很短暂。以下是在<pre>
代码中保留代码的解决方法:
<?php
$test = file_get_contents('input.html');
$dom = new DOMDocument('1.0');
$dom->loadHTML($test);
$xpath = new DOMXpath($dom);
$pre = $xpath->query('//pre//text()');
// manipulate nodes of type XML_TEXT_NODE
foreach($pre as $e) {
$e->nodeValue = str_replace(' ', '__REPLACEMELATER__', $e->nodeValue);
// when you attempt to write in a dom node
// the & will be converted to & :(
}
$temp = $dom->saveHTML();
$temp = str_replace('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">', '', $temp);
$temp = str_replace('<html>', '', $temp);
$temp = str_replace('<body>', '', $temp);
$temp = str_replace('</body>', '', $temp);
$temp = str_replace('</html>', '', $temp);
$temp = str_replace('__REPLACEMELATER__', ' ', $temp);
echo $temp;
?>
<p>paragraph 1 remains untouched</p>
<pre>preformatted 1</pre>
<div>
<pre>preformatted 2</pre>
</div>
<div>
<pre>preformatted 3 <span class="foo">span text</span> preformatted 3</pre>
</div>
<div>
<pre>preformatted 4 <span class="foo">span <b class="bla">bold test</b> text</span> preformatted 3</pre>
</div>
<p>paragraph 1 remains untouched</p>
<pre>preformatted 1</pre>
<div>
<pre>preformatted 2</pre>
</div>
<div>
<pre>preformatted 3 <span class="foo">span text</span> preformatted 3</pre>
</div>
<div>
<pre>preformatted 4 <span class="foo">span <b class="bla">bold test</b> text</span> preformatted 3</pre>
</div>
DOMDocument::saveHTML()
方法&gt; = 5.3.6允许您指定要输出的节点。否则,您可以使用str_replace()
或preg_replace()
来删除doctype,html和body标记。
这个技巧似乎有效,并且只产生一行代码,但我不确定它是否可以保证工作:
$e->nodeValue = utf8_encode(str_replace(' ', "\xA0", $e->nodeValue));
// dom library will attempt to convert 0xA0 to
// nodeValue expects utf-8 encoded data but 0xA0 is not valid in this encoding
// hence replaced string must be utf-8 encoded