我正在尝试使用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的建议(见下文),我如何完成解析主元素和子元素的属性?
答案 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格式的数据要小得多,我想你不会因为这个事实而遇到相同的内存问题。