如何使用PHP按顺序解析大型XML文件

时间:2010-11-08 20:03:06

标签: php xml memory-management

我正在尝试使用simpleXML在php中解析一个中等大小的XML文件(6mb)。该脚本从XML文件中获取每条记录,检查它是否已经被导入,如果没有,则将该记录更新/插入到我自己的数据库中。

问题是我经常遇到超出内存分配的致命错误:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 256 bytes) in /.../system/database/drivers/mysql/mysql_result.php on line 162

我通过使用以下行来增加最大内存分配(从here开始提示)后避免了这个错误:

ini_set('memory_limit', '-1');

但是,然后我遇到60秒的最大执行时间,并且,无论出于何种原因,我的服务器(Mac OS X上的XAMPP)都不会让我增加那个时间(如果我的话,脚本就不会运行尝试包括如下行:)

set_time_limit(240);
然而,这一切看起来都非常低效;我不应该能够打破文件的顺序并按顺序处理它吗?在下面的控制器中,我有一个计数变量($ cycle)来跟踪我正在使用的记录,但我无法弄清楚如何实现它仍然不需要处理整个XML文件。

控制器(我正在使用CodeIgniter)具有以下基本结构:

    $f = base_url().'data/data.xml';
    if($data = file_get_contents($f))
    {
        $cycle = 0;
        $xml = new SimpleXMLElement($data);
        foreach($xml->person as $p)
        {

        //this makes a single call to db for single field based on id of record in XML file                
        if($this->_notImported('source',$p['id']))
            {
               //various process here, mainly breaking up the data for inserting into four different bales
            }
            $cycle++;
        }
    }

有什么想法吗?

被修改

为了进一步了解我正在做的事情,我抓住了每个元素和子元素的大部分属性,并将它们插入到我的数据库中。例如,使用我的旧代码,我有类似的东西:

$insert = array('indiv_name' => $p['fullname'],
                                    'indiv_first' => ($p['firstname']),
                                    'indiv_last' => ($p['lastname']),
                                    'indiv_middle' => ($p['middlename']),
                                    'indiv_other' => ($p['namemod']),
                                    'indiv_full_name' => $full_name,
                                    'indiv_title' => ($p['title']),
                                    'indiv_dob' => ($p['birthday']),
                                    'indiv_gender' => ($p['gender']),
                                    'indiv_religion' => ($p['religion']),
                                    'indiv_url' => ($url)
                                    );

根据使用XMLReader的建议(见下文),我如何完成解析主元素和子元素的属性?

3 个答案:

答案 0 :(得分:5)

使用XMLReader

说你的文件是这样的:

<test>
   <hello>world</hello>
   <foo>bar</foo>
</test>

使用XMLReader:

$xml = new XMLReader;
$xml->open('doc.xml');

$xml->read();
while ($xml->read()) {
        if ($xml->nodeType == XMLReader::ELEMENT) {
                print $xml->name.': ';
        } else if ($xml->nodeType == XMLReader::TEXT) {
                print $xml->value.PHP_EOL;
        }
}

输出:

hello: world
foo: bar

不错的是,您还可以使用expand将节点作为DOMNode对象获取。

答案 1 :(得分:4)

听起来问题是你在尝试操作它之前将整个xml文件读入内存。使用XMLReader遍历文件流,而不是将所有内容加载到内存中进行操作。

答案 2 :(得分:1)

如何使用json而不是使用xml? JSON格式的数据要小得多,我想你不会因为这个事实而遇到相同的内存问题。