4

XMLReader 可以处理的最大文件大小是否存在?

我正在尝试处理大约 3GB 的 XML 提要。当然没有 PHP 错误,因为脚本运行良好并在运行后成功加载到数据库。

该脚本还可以在较小的测试提要(1GB 及以下)上正常运行。但是,当处理较大的提要时,脚本会在大约 1GB 后停止读取 XML 文件,并继续运行脚本的其余部分。

有没有人遇到过类似的问题?如果是这样,您是如何解决的?

提前致谢。

4

6 回答 6

2

我最近遇到了同样的问题,我想分享我的经验。

似乎问题在于 PHP 的编译方式,无论是编译时支持 64 位文件大小/偏移量还是仅使用 32 位。

使用 32 位,您只能处理 4GB 的数据。您可以在这里找到一些令人困惑但很好的解释:http: //blog.mayflower.de/archives/131-Handling-large-files-without-PHP.html

我必须使用 Perl 实用程序拆分文件xml_split,您可以在此处找到:http ://search.cpan.org/~mirod/XML-Twig/tools/xml_split/xml_split

我用它把我巨大的 XML 文件分割成可管理的块。该工具的好处在于它将 XML 文件拆分为整个元素。不幸的是它不是很快。

我只需要这样做一次,它适合我的需要,但我不建议重复使用它。1GB拆分后,我在大约大小的较小文件上使用了 XMLReader 。

于 2012-01-20T00:58:42.310 回答
1

拆分文件肯定会有所帮助。其他可以尝试的...

  1. 调整 php.ini 中的 memory_limit 变量。http://php.net/manual/en/ini.core.php
  2. 使用 SAX 重写您的解析器 - http://php.net/manual/en/book.xml.php。这是一个面向流的解析器,不需要解析整个树。内存效率更高,但更难编程。

根据您的操作系统,您可以分配的 RAM 块也可能有 2gb 的限制。如果您在 32 位操作系统上运行,则很有可能。

于 2010-08-06T15:05:37.110 回答
1

应该注意的是,PHP 通常有一个最大文件大小。PHP 不允许使用无符号整数或长整数,这意味着整数的上限为 2^31(或 64 位系统为 2^63)。这很重要,因为 PHP 使用整数作为文件指针(您在通读时在文件中的位置),这意味着它无法处理大小超过 2^31 字节的文件。

但是,这应该超过 1 GB。我遇到了 2 GB 的问题(正如预期的那样,因为 2^31 大约是 20 亿)。

于 2010-08-11T21:16:07.927 回答
0

使用 WindowsXP、NTFS 作为文件系统和 php 5.3.2,这个测试脚本没有问题

<?php
define('SOURCEPATH', 'd:/test.xml');

if ( 0 ) {
  build();
}
else {
  echo 'filesize: ', number_format(filesize(SOURCEPATH)), "\n";
  timing('read');
}

function timing($fn) {
  $start = new DateTime();
  echo 'start: ', $start->format('Y-m-d H:i:s'), "\n";
  $fn();
  $end = new DateTime();
  echo 'end: ', $start->format('Y-m-d H:i:s'), "\n";
  echo 'diff: ', $end->diff($start)->format('%I:%S'), "\n";
}

function read() {
  $cnt = 0;
  $r = new XMLReader;
  $r->open(SOURCEPATH);
  while( $r->read() ) {
    if ( XMLReader::ELEMENT === $r->nodeType ) {
      if ( 0===++$cnt%500000 ) {
        echo '.';
      }
    }
  }
  echo "\n#elements: ", $cnt, "\n";
}

function build() {
  $fp = fopen(SOURCEPATH, 'wb');

  $s = '<catalogue>';
  //for($i = 0; $i < 500000; $i++) {
  for($i = 0; $i < 60000000; $i++) {
    $s .= sprintf('<item>%010d</item>', $i);
    if ( 0===$i%100000 ) {
      fwrite($fp, $s);
      $s = '';
      echo $i/100000, ' ';
    }
  }

  $s .= '</catalogue>';
  fwrite($fp, $s);
  flush($fp);
  fclose($fp);
}

输出:

filesize: 1,380,000,023
start: 2010-08-07 09:43:31
........................................................................................................................
#elements: 60000001
end: 2010-08-07 09:43:31
diff: 07:31

(如您所见,我搞砸了结束时间的输出,但我不想再运行这个脚本超过 7 分钟 ;-))

这也适用于您的系统吗?


附带说明:相应的 C# 测试应用程序只用了 41 秒而不是 7.5 分钟。在这种情况下,我的慢速硬盘可能是/一个限制因素。

filesize: 1.380.000.023
start: 2010-08-07 09:55:24
........................................................................................................................

#elements: 60000001

end: 2010-08-07 09:56:05
diff: 00:41

和来源:

using System;
using System.IO;
using System.Xml;

namespace ConsoleApplication1
{
  class SOTest
  {
    delegate void Foo();
    const string sourcepath = @"d:\test.xml";
    static void timing(Foo bar)
    {
      DateTime dtStart = DateTime.Now;
      System.Console.WriteLine("start: " + dtStart.ToString("yyyy-MM-dd HH:mm:ss"));
      bar();
      DateTime dtEnd = DateTime.Now;
      System.Console.WriteLine("end: " + dtEnd.ToString("yyyy-MM-dd HH:mm:ss"));
      TimeSpan s = dtEnd.Subtract(dtStart);
      System.Console.WriteLine("diff: {0:00}:{1:00}", s.Minutes, s.Seconds);
    }

    static void readTest()
    {
      XmlTextReader reader = new XmlTextReader(sourcepath);
      int cnt = 0;
      while (reader.Read())
      {
        if (XmlNodeType.Element == reader.NodeType)
        {
          if (0 == ++cnt % 500000)
          {
            System.Console.Write('.');
          }
        }
      }
      System.Console.WriteLine("\n#elements: " + cnt + "\n");
    }

    static void Main()
    {
      FileInfo f = new FileInfo(sourcepath);
      System.Console.WriteLine("filesize: {0:N0}", f.Length);
      timing(readTest);
      return;
    }
  }
}
于 2010-08-07T08:00:35.960 回答
0

你有任何错误吗

libxml_use_internal_errors(true);
libxml_clear_errors();

// your parser stuff here....    
$r = new XMLReader(...);
// ....


foreach( libxml_get_errors() as $err ) {
   printf(". %d %s\n", $err->code, $err->message);
}

当解析器过早停止?

于 2010-08-06T15:44:54.000 回答
0

我在解析大型文档时遇到了类似的问题。我最终做的是使用文件系统函数将提要分成更小的块,然后解析这些更小的块......所以如果你有一堆<record>要解析的标签,用字符串函数将它们作为流解析出来,当你在缓冲区中获取完整记录,使用 xml 函数对其进行解析......这很糟糕,但它工作得很好(并且非常节省内存,因为您在任何时候最多只有 1 条记录在内存中)......

于 2010-08-06T14:53:45.863 回答