SAX Parser PHP在x行没有内存

时间:2014-07-30 16:39:42

标签: php xml memory sax filesize

我使用带有php的SAX Parser解析一个1.2GB的xml文件,结果写在一个txt文件中。几分钟后,我得到一个"在第x和34行没有记忆的错误。完成此操作后,文本文件输出仅为几KB(而预期的输出文件大小可能会超过几MB)并且存储的信息非常少。我使用xampp在带有i7处理器和18 GB RAM的Windows 7 64位系统上运行这个项目,但我也在32位系统上运行它,结果相同。我用这些命令修改了php.ini:

ini_set('max_execution_time', 72000); 
ini_set('memory_limit','13056M');
ini_set('realpath_cache_size','13056000k');
ini_set('max_input_time','-1');

我应该在php.ini中修改一些内容吗?当代码没有找到要解析的过滤数据并且只读取" junk"这可能是某种缓冲区溢出。数据?这可能是由OS文件大小限制引起的错误吗?有没有人解决过类似的问题? 提前谢谢。

3 个答案:

答案 0 :(得分:1)

问题可能是您用来解析xml文件的函数。解析具有错误函数的大型xml文件需要大量内存,因为xml文档是"后退"在返回结果之前在系统内存中。

我非常熟悉php的XMLReader类来解析大型xml文件。

http://php.net/manual/en/class.xmlreader.php

请不要增加memory_limit来解决此问题,我认为这是解决错误的错误方法。

肯定你没有向我们展示你的代码的整个实现,问题也可以通过你在php中进行进一步处理数据的方式找到(制作一个大型阵列,不要{t {1}}变量)很难分辨。

答案 1 :(得分:1)

我遇到了同样的问题,到目前为止我的调查指出了使用的libxml2版本。高于2.7.6的版本似乎对内存使用有限制,除非您明确允许它使用超过硬编码限制(LIBXML_PARSEHUGE)。

查看http://php.net/manual/en/libxml.constants.php了解详情。

不幸的是,我还没有找到解决问题的方法,即在PHP中使用XML Parser函数时如何设置此选项。我猜xml_parser_set_option会是正确的地方吗?

目前似乎唯一的选择是更新代码以使用XMLReader。此扩展在open函数中有option参数,您可以在其中提供libxml选项常量。

更新!

查看xml扩展的源代码,我发现xml_parse函数正在使用不带选项的libxml函数xmlParseChunk。我能够通过减少块大小而不提供此选项来使其工作(xml_parse失败,并且#39;没有内存'当块大小超过9.5MB时)。也许你可以试试这个:

while ($data = fread($fp, 1024*1024)) {
  xml_parse($parser, $data, feof($fp)) or
    die(sprintf('XML ERROR: %s at line %d',
    xml_error_string(xml_get_error_code($parser)),
    xml_get_current_line_number($parser)));
}

答案 2 :(得分:0)

我有一个数组,但我不认为它变得太大了。它必不​​可少我不清楚那个数组。没有会话只使用简单的字符串变量。

我正在解析从官方来源下载的dblp.xml文件,因此很少有机会形成xml(也包含有效的dtd)。

我不想发布代码因为它有点大但是在这里:

ini_set('max_execution_time', 72000);

$professors = array();

$parser = xml_parser_create();

xml_set_element_handler($parser, 'start_element', 'end_element');
xml_set_character_data_handler($parser, 'character_data');

function start_element($parser, $element_name, $element_attrs) {

global $interest;
global $author_id;
global $id;
global $author;
global $no_url;
global $url;
global $professors;
global $counter;
global $author_name;

switch ($element_name) {
    case 'WWW':
        $counter = 0;
        $duplicate = false;
        $author_id = end($element_attrs);

        if (substr($author_id, 0, 9) === 'homepages') {
            for ($i = 0; $i < count($professors); $i++) {
                if ($author_id === $professors[$i]) {
                    $duplicate = true;
                }
            }

            if ($duplicate) {
                $id = false;
                $interest = false;
            }
            else {
                array_push($professors, $author_id);
                $id = true;
                $interest = true;
            }
        }
        break;
    case 'AUTHOR':
        $author_name = "";
        $author = true;
        $no_url = true;
        break;
    case 'URL':
        $url = true;
        $no_url = false;
        break;
}
}

function character_data($parser, $data) {

$fp1 = fopen("Authors.txt","a+");

global $interest;
global $author_id;
global $id;
global $author;
global $no_url;
global $url;
global $counter;
global $author_name;

if ($interest && $id) {
    if ($no_url) {
        fwrite($fp1,"*" . "\r\n");
        fwrite($fp1,$author_id);
    }
    else {
        fwrite($fp1,$author_id);
    }
    $id = false;
}

if ($interest && $author) {

    $author_name .= $data;
}

if ($interest && $url) {

    $url_name = $data;
    fwrite($fp1,"*" . $url_name . "\r\n");
}
}

function end_element($parser, $element_name) {

global $interest;
global $author_id;
global $id;
global $author;
global $url;
global $author_name;

$fp1 = fopen("Authors.txt","a+");   

switch ($element_name) {
    case 'WWW':
        $interest = false;
        break;
    case 'AUTHOR':
        if ($interest && $author) {
            fwrite($fp1,"*" . $author_name);
            $author = false;
        }
        break;
    case 'URL':
        $url = false;
        break;
    case 'DBLP':
        die(sprintf('End of XML'));
        xml_parser_free($parser);
        break;
}
}

$fp = fopen('dblp.xml', 'r')
  or die ("Cannot open dblp.xml!");

while ($data = fread($fp, 100000000)) {  //i give such a huge number to read each time to cover the case of a large line in the xml.i get the same no memory at line error though with the default 4096 value as well
  xml_parse($parser, $data) or
    die(sprintf('XML ERROR: %s at line %d',
    xml_error_string(xml_get_error_code($parser)),
    xml_get_current_line_number($parser)));
}

xml_parser_free($parser);