[英]Complex edit xml file
例如,我们有以下xml:
<x>
<y>some text</y>
<y>[ID] hello</y>
<y>world [/ID]</y>
<y>some text</y>
<y>some text</y>
</x>
并且我们需要删除单词“ [[ID]”,“ [/ ID]”和它们之间的文本(在解析时我们不知道),当然不会损坏xml格式。
我能想到的唯一解决方案是:
使用正则表达式在xml中查找文本,例如: "/\\[ID\\].*?\\[\\/ID\\]/"
。 在我们的例子中,结果将是"[ID]hello</y><y>world[/ID]"
上一步的结果是,我们需要使用以下正则表达式查找不带xml标签的文本: "/(?<=^|>)[^><]+?(?=<|$)/"
,然后删除此文本。 结果将是"</y><y>"
通过像这样进行smth在原始xml中进行更改:
str_replace($step1string,$step2string,$xml);
这是正确的方法吗? 我只是认为这种“ str_replace”并不是最佳的编辑xml的方法,所以也许您知道更好的解决方案?
为了娱乐和娱乐,您可能需要阅读以下内容: RegEx匹配除XHTML自包含标签以外的其他开放标签
“正确”的解决方案是使用XML库并搜索节点以执行操作。 但是,即使有可能损坏XML格式,仅使用str_replace可能也会容易得多。 您必须评估收到类似<a href="[ID]">
类的内容的可能性,以及防范此类情况的重要性,并权衡这些因素与开发时间。
删除特定的字符串很简单:
<?php
$xml = '<x>
<y>some text</y>
<y>[ID] hello</y>
<y>world [/ID]</y>
<y>some text</y>
<y>some text</y>
</x>';
$d = new DOMDocument();
$d->loadXML($xml);
$x = new DOMXPath($d);
foreach($x->query('//text()[(contains(.,\'[ID]\') or contains(.,\'[/ID]\'))]') as $elm){
$elm->nodeValue = preg_replace('/\[\/?ID\]/','',$elm->nodeValue);
}
var_dump($d->saveXML());
?>
当只删除特定标签中的textnode时,可以将te preg_replace更改为以下两个:
$elm->nodeValue = preg_replace('/\[ID\].*$/','',$elm->nodeValue);
$elm->nodeValue = preg_replace('/^.*\[/ID\]/','',$elm->nodeValue);
结果为您的示例:
<x>
<y>some text</y>
<y></y>
<y></y>
<y>some text</y>
<y>some text</y>
</x>
但是,在不破坏格式正确的XML的情况下删除它们之间的标记非常棘手。 在尝试大量DOM操作之前,您要如何处理:
DOM树中较高的[/ ID]:
<foo>[ID] foo
<bar> lorem [/ID] ipsum </bar>
</foo>
DOM树中较低的[/ ID]
<foo> foo
<bar> lorem [ID] ipsum </bar>
[/ID]
</foo>
并按照您的示例打开/关闭跨越的同级:
<foo> foo
<bar> lorem [ID] ipsum </bar>
<bar> lorem [/ID] ipsum </bar>
</foo>
一个真正的难题突破了:嵌套是否可能,嵌套是否格式正确,它应该怎么做?
<foo> foo
<bar> lo [ID] rem [ID] ipsum </bar>
<bar> lorem [/ID] ipsum </bar>
[/ID]
</foo>
没有进一步的知识应该如何处理这些案件,就没有真正的答案。
编辑,并提供了更多的信息,实际的故障安全解决方案(例如:解析XML,不使用正则表达式)似乎很长,但可以在99.99%的情况下使用(当然,不包括个人错别字和脑筋急转弯:) ):
<?php
$xml = '<x>
<y>some text</y>
<y>
<a> something </a>
well [ID] hello
<a> and then some</a>
</y>
<y>some text</y>
<x>
world
<a> also </a>
foobar [/ID] something
<a> these nodes </a>
</x>
<y>some text</y>
<y>some text</y>
</x>';
echo $xml;
$d = new DOMDocument();
$d->loadXML($xml);
$x = new DOMXPath($d);
foreach($x->query('//text()[contains(.,\'[ID]\')]') as $elm){
//if this node also contains [/ID], replace and be done:
if(($startpos = strpos($elm->nodeValue,'[ID]'))!==false && $endpos = strpos($elm->nodeValue,'[/ID]',$startpos)){
$elm->replaceData($startpos, $endpos-$startpos + 5,'');
var_dump($d->saveXML($elm));
continue;
}
//delete all siblings of this textnode not being text and having [/ID]
while($elm->nextSibling){
if(!($elm->nextSibling instanceof DOMTEXT) || ($pos =strpos($elm->nodeValue,'[/ID]'))===false){
$elm->parentNode->removeChild($elm->nextSibling);
} else {
//id found in same element, replace and go to next [ID]
$elm->parentNode->appendChild(new DOMTExt(substr($elm->nextSibling->nodeValue,$pos+5)));
$elm->parentNode->removeChild($elm->nextSibling);
continue 2;
}
}
//siblings of textnode deleted, string truncated to before [ID], now let's delete intermediate nodes
while($sibling = $elm->parentNode->nextSibling){ // in case of example: other <y> elements:
//loop though childnodes and search a textnode with [/ID]
while($child = $sibling->firstChild){
//delete if not a textnode
if(!($child instanceof DOMText)){
$sibling->removeChild($child);
continue;
}
//we have text, check for [/ID]
if(($pos = strpos($child->nodeValue,'[/ID]'))!==false){
//add remaining text in textnode:
$elm->appendData(substr($child->nodeValue,$pos+5));
//remove current textnode with match:
$sibling->removeChild($child);
//sanity check: [ID] was in <y>, is [/ID]?
if($sibling->tagName!= $elm->parentNode->tagname){
trigger_error('[/ID] found in other tag then [/ID]: '.$sibling->tagName.'<>'.$elm->parentNode->tagName, E_USER_NOTICE);
}
//add remaining childs of sibling to parent of [ID]:
while($sibling->firstChild){
$elm->parentNode->appendChild($sibling->firstChild);
}
//delete the sibling that was found to hold [/ID]
$sibling->parentNode->removeChild($sibling);
//done: end both whiles
break 2;
}
//textnode, but no [/ID], so remove:
$sibling->removeChild($child);
}
//no child, no text, so no [/ID], remove:
$elm->parentNode->parentNode->removeChild($sibling);
}
}
var_dump($d->saveXML());
?>
我能想到的唯一其他选择是,是否可以将XML格式设置为不同格式。
<x>
<y>
<z>[ID]</z>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.