在我最后一个问题的后续中,如果xml文件中有一个格式错误的字符串,可以使用preg_replace_callback()提取内容,以删除中断的元素。
这个函数的目的不是用regex解析xml(a坏idea),但为了找到不解析的xml以及它失败的地方可以标记未正确格式化的文章发送出去。这是一组工具的一部分,用于在传送我也在已知的格式错误的公共RSS URL上测试它作为内部的,看看它是否适合多种情况。回调将为失败的节点返回一个整数。如果之后通过,我们可以报告文章的索引,然后尝试使用DOMDocument来更正html,然后重试。如果失败,我们将报告为关键,否则,我们将解析文章描述和内容返回到数据库,并在交付前将其标记为已修改。
然后,您可以获取损坏的元素,并通过DOMDocument运行它们,以更好地格式化它们,从而返回到XML文件。
然而,我被困在如何使下面的例子返回而不是错误:
示例XML:
<item>
<content:encoded><![CDATA[
This is the text with odd characters that are killing
simplexml_load_string() (doesn't recover) and breaking
(although recoverable) DOMDocument
]]></content:encoded>
</item>
如果我使用以下PHP,我可以提取一个描述节点并将其转换为:
<description><![CDATA[
This is some description text with the same problem
]]></description>
至
<description>0</description>
PHP:
preg_replace_callback(
'/<description>(.*)<'/description>/', **// add msU modifiers to fix below**
'node_tidy::callback_description',
$xml
);
private function callback_description($matches=false) {
if(false !== $matches) {
$this->arrDescriptions[] = $matches[1];
return '<description>'.$this->indexDescriptions++.'</description>';
} else {
return false;
}
}
但是,当我尝试对content:encoded
节点执行同样的操作时,它会返回false。以下是相关功能:
private function callback_content_encoded($matches=false) {
if(false !== $matches) {
$this->arrContentEncoded[] = $matches[1];
return '<content:encoded>'.$this->indexContentEncoded++.'</content:encoded>';
} else {
return false;
}
}
使用一个直接的正则表达式来测试它是否是冒号,我使用了这个:
<?php
$string = '<content:encoded>this is some text</content:encoded>';
preg_match('/<content':encoded>(.*)<'/content':encoded>/',$string,$matches);
echo '<pre>';
print_r($matches);
echo '</pre>';
?>
但是,无论是否添加':
,都不会打印预期的数组。有人能为我指出这里的误解的正确方向吗?
非常感谢!
更新:下面是一个失败的真实xml的示例片段,如@Florent所示。
http://pastebin.com/7z0f3MJP
更新:此正则表达式匹配所需内容:
preg_match('/<content':encoded>(.*)<'/content':encoded>/msU',$string,$matches);
m、s和U修饰符在这里得到了更好的解释:http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
我忽略了考虑这些修饰语。
结果现在由这个正则表达式返回,包括原来的问题,所以现在可以解决了。
您应该将以下标志添加到正则表达式中:
m
启用多行字符串u
启用UTF8字符串(如有必要)
没有使用多行修饰符,因此不需要它。只需要/s(点全部)修饰符。/U(非贪婪)修饰符永远不应该使用(在我看来)。应该使用/u(unicode)修饰符。
如果您希望在CDATA结构中取消包装html,最好使用w3c规范,也就是说,即使您的xml使用名称空间名称作为其标记。只有当xml标记中唯一的元素是CDATA,并且假设xml格式正确时,才会出现这种情况。
在现实世界中,评论可以包装CDATA,反之亦然,还可以隐藏许多其他内容。因此,现实情况是regex可能能够通过格式错误的xml进行解析,然后进行恢复,但它并不可靠,而且肯定更复杂。
话虽如此,这将从您的示例中提取CDATA,并且仅从字面意义上提取CDATA。
if (preg_match(
'~<content:encoded's*>
's*
<!'[CDATA'[ (.*?) ']']>
's*
</content:encoded's*>~xsu',
$string,
$matches) )
{
print ( $matches[1] );
}