使用正则表达式解析XML/XHTML数据

3
我已经阅读了这篇著名的帖子,也见过尝试的结果(有成功也有失败)。在这里和其他地方都有激烈的口水战。
但是它是可以做到的。
虽然我知道实际的论点(读作事实)是正则表达式不适用于解析结构化数据树,因为它们不能监视和更改状态,但我觉得有些人盲目地丢弃了这种可能性。应用程序逻辑是必要的,以保持状态,但正如这个有效的示例所显示的那样,它是可以做到的。
相关片段如下:
const PARSE_MODE_NEXT = 0;
const PARSE_MODE_ELEMENT = 1;
const PARSE_MODE_ENTITY = 3;
const PARSE_MODE_COMMENT = 4;
const PARSE_MODE_CDATA = 5;
const PARSE_MODE_PROC = 6;

protected $_parseModes = array(
        self::PARSE_MODE_NEXT     => '% < (?: (?: (?<entity>!) (?: (?<comment>--) | (?<cdata>\[CDATA\[) ) ) | (?<proc>\?) )? %six',
        self::PARSE_MODE_ELEMENT  => '% (?<close>/)? (?<element> .*? ) (?<empty> / )? > (?<text> [^<]* ) %six',
        self::PARSE_MODE_ENTITY   => '% (?<entity> .*? ) > (?<text> [^<]* ) %six',
        self::PARSE_MODE_COMMENT  => '% (?<comment> .*? ) --> (?<text> [^<]* ) %six',
        self::PARSE_MODE_CDATA    => '% (?<cdata> .*? ) \]\]> (?<text> [^<]* ) %six',
        self::PARSE_MODE_PROC     => '% (?<proc> .*? ) \?> (?<text> [^<]* ) %six',
    );

public function load($string){
    $parseMode = self::PARSE_MODE_NEXT;
    $parseOffset = 0;
    $context = $this;
    while(preg_match($this->_parseModes[$parseMode], $string, $match, PREG_OFFSET_CAPTURE, $parseOffset)){
        if($parseMode == self::PARSE_MODE_NEXT){
            switch(true){
                case (!($match['entity'][0] || $match['comment'][0] || $match['cdata'][0] || $match['proc'][0])):
                    $parseMode = self::PARSE_MODE_ELEMENT;
                    break;
                case ($match['proc'][0]):
                    $parseMode = self::PARSE_MODE_PROC;
                    break;
                case ($match['cdata'][0]):
                    $parseMode = self::PARSE_MODE_CDATA;
                    break;
                case ($match['comment'][0]):
                    $parseMode = self::PARSE_MODE_COMMENT;
                    break;
                case ($match['entity'][0]):
                    $parseMode = self::PARSE_MODE_ENTITY;
                    break;
            }
        }else{
            switch($parseMode){
                case (self::PARSE_MODE_ELEMENT):
                    switch(true){
                        case (!($match['close'][0] || $match['empty'][0])):
                            $context = $context->addChild(new ZuqMLElement($match['element'][0]));
                            break;
                        case ($match['empty'][0]):
                            $context->addChild(new ZuqMLElement($match['element'][0]));
                            break;
                        case ($match['close'][0]):
                            $context = $context->_parent;
                            break;
                    }
                    break;
                case (self::PARSE_MODE_ENTITY):
                    $context->addChild(new ZuqMLEntity($match['entity'][0]));
                    break;
                case (self::PARSE_MODE_COMMENT):
                    $context->addChild(new ZuqMLComment($match['comment'][0]));
                    break;
                case (self::PARSE_MODE_CDATA):
                    $context->addChild(new ZuqMLCharacterData($match['cdata'][0]));
                    break;
                case (self::PARSE_MODE_PROC):
                    $context->addChild(new ZuqMLProcessingInstruction($match['proc'][0]));
                    break;
            }
            $parseMode = self::PARSE_MODE_NEXT;
        }
        if(trim($match['text'][0])){
            $context->addChild(new ZuqMLText($match['text'][0]));
        }
        $parseOffset = $match[0][1] + strlen($match[0][0]);
    }

}

这是完整的吗?不是。

它是不可破坏的吗?当然不是。

它快吗?没有进行基准测试,但我无法想象它会像DOM一样快。

它支持XPath/XQuery吗?显然不支持。

它验证或执行任何其他辅助任务吗?当然不。

它会取代DOM吗?绝对不会。

然而,它能解析这个吗?

<?xml version="1.0" encoding="utf-8"?>
<!ENTITY name="value">
<root>
    <node>
        <node />
        Foo
        <node name="value">
            <node>Bar</node>
        </node>
        <!-- Comment -->
    </node>
    <node>
        <[CDATA[ Character Data ]]>
    </node>
</root>

是的,它可以。

虽然我希望这个主题成为社区Wiki,但只要满足要求,我会把这个声明变成一个问题。

关注正则表达式,有人能预见在使用良好格式的标记时,是否会出现严重失败的情况?我认为我已经全面考虑了。

我没有煽动事端的意图,但我想听取双方的见解。

还要注意的是,我写这篇文章的目的是因为SimpleXML太简单,而DOM对于我的某些应用程序来说太严格了。

1个回答

1
专注于正则表达式,有人能预见在使用它来处理格式良好的标记时会出现什么严重问题吗?当针对XML一致性测试套件运行时,它拒绝了多少格式良好的XML文档,接受了多少格式不良的XML文档?
也许最大的反对意见来自那些分享XML社区文化的人,即它不仅可以解析大多数格式良好的XML文档,还可以解析大多数非XML文档,因为它不会告诉您它们格式不良。也许你认为在你的环境中这并不太重要,但最终,如果你接受格式不良的文档,那么人们就会开始向你发送格式不良的文档,不久之后,你就会陷入与HTML相同的困境,必须出于遗留原因接受任何旧垃圾。
我不了解足够的PHP来快速判断您的代码在处理格式良好的XML方面的效果如何。但我质疑动机-为什么你想手写一个便宜、肮脏、慢的XML解析器,而市面上已经有完全正确、快速、免费的解析器可用呢?

虽然你提出了一些无可辩驳的观点,但我写这篇文章的主要原因是:SimpleXML太简单了,对我所需的内容支持有限;DOM太严格了,导致解决问题比它带来更多问题;我需要一些迁移友好型的东西,可以轻松地包含在任何项目中,而不受环境限制;对于它的目的,我不关心验证,只关心解析和重构结构化文档的能力。然而,速度是一个问题,超过DOM 30倍以上(100,000次测试迭代),它失败了,无法使用。:( - Dan Lugg
感谢您的评论,Michael Kay! - Dan Lugg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接