【问题标题】:How to use PHP to parse large XML file sequentially如何使用 PHP 顺序解析大型 XML 文件
【发布时间】:2011-05-06 20:48:28
【问题描述】:

我正在尝试使用 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 的建议(见下文),我该如何完成对主元素和子元素的属性的解析?

【问题讨论】:

    标签: php xml memory-management


    【解决方案1】:

    使用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 对象获取。

    【讨论】:

    • 谢谢——这个答案真的很有帮助。但是,如何访问子元素的属性?每个元素都有可变数量的子元素,我需要抓取每个元素的属性。
    • 有很多方法可以做到这一点。最简单的是getAttribute('attr_name')。你也可以在expand 之后使用moveToNextAttributeDOMNode::$attributes。但是,我真的认为第一个选择是要走的路。 ;)
    • 谢谢!当我使用 getAttribute 时,它​​会立即返回该属性的每个实例。例如,一些元素有 10 个子元素,每个子元素都有一个“startdate”属性,使用 getAttribute 会同时返回所有十个日期。但我一次只需要访问它们一个。我如何一次处理一个?
    • 我不知道该告诉你什么。 getAttribute 一次只返回一个属性。可能是循环或其他问题。我可以建议您使用更新的代码创建一个新问题。 :)
    • 完成 (stackoverflow.com/questions/4129565/…)。再次感谢所有帮助。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      不使用xml,使用json怎么样? JSON 格式的数据会小得多,因此我想您不会遇到同样的内存问题。

      【讨论】:

      • 感谢您的建议 -- 如何将 XML 转换为 JSON?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-22
      • 2012-01-27
      • 1970-01-01
      • 1970-01-01
      • 2011-05-09
      • 2012-02-05
      • 1970-01-01
      相关资源
      最近更新 更多