当php和simplexml_load_string解析失败时,xml标记中冒号的Regex


Regex for colon in an xml tag when parsing fails with php and simplexml_load_string

在我最后一个问题的后续中,如果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] );
}