Php 使用XMLReader读取大于1GB的文件时出现问题

Php 使用XMLReader读取大于1GB的文件时出现问题,php,file,size,max,xmlreader,Php,File,Size,Max,Xmlreader,是否存在XMLReader可以处理的最大文件大小 我正在尝试处理一个大约3GB的XML提要。当然没有PHP错误,因为脚本运行良好,并在运行后成功加载到数据库 该脚本在较小的测试源(1GB及以下)中也可以正常运行。但是,当处理较大的提要时,脚本在大约1GB后停止读取XML文件,并继续运行脚本的其余部分 有没有人经历过类似的问题?如果是的话,你是如何解决的 提前感谢。我在解析大型文档时遇到了类似的问题。最后我做的是使用文件系统函数将提要分解成更小的块,然后解析那些更小的块。。。因此,如果您有一堆正在

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

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

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

有没有人经历过类似的问题?如果是的话,你是如何解决的


提前感谢。

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

拆分文件肯定会有帮助。其他要尝试的东西

  • 调整php.ini中的memory_limit变量
  • 使用SAX重写解析器--。这是一个面向流的解析器,不需要解析整个树。内存效率更高,但编程难度稍大

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

    您是否发现任何错误

    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);
    }
    

    当解析器过早停止时

    使用WindowsXP、NTFS作为文件系统和PHP5.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);
    }
    
    (如您所见,我把结束时间的输出搞砸了,但我不想再运行这个脚本7分钟以上;-))

    这也适用于您的系统吗


    附带说明:相应的C#test应用程序只花了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;
        }
      }
    }
    

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


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

    最近我遇到了类似的问题,我想分享一下我的经验

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

    32位只能寻址4GB的数据。您可以在这里找到一些令人困惑但很好的解释:

    我必须使用Perl实用工具
    xml\u split
    拆分文件,您可以在这里找到:

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


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

    您确定没有生成PHP错误吗?工作和不工作之间的决定性因素究竟是什么(据你所知)?“脚本”看起来像什么,除了在XML上迭代之外,它还做了什么?在伪代码中,脚本看起来像$this->downloadFeed();尝试{$this->writeXMLFeedToCSV();}catch(e){//handle exception}$this->uploadCSVToDatabaseTable();如果脚本由于PHP错误而失败,它将不会上载到数据库。目前确实如此。xml的格式也是正确的,就像ircmaxell所说的,当脚本崩溃时,它可以正常工作。然而,这个过程是乏味的,我们希望找到一个解决办法。很抱歉,由于信息的性质,我不能随意分享脚本。您使用哪一个操作系统b)文件系统c)php版本d)php版本进行测试?谢谢,是的,我最后也是这么做的。但是,正如您所提到的,它很糟糕:o)您是否碰巧知道xml阅读器是否有可以读取的最大文件大小?再次感谢您的建议,我发现了错误的来源和一个迄今为止一直对我有效的解决方案,并认为您可能能够实现它。事实证明,提要中有一个垂直选项卡(^K或char 11),它不是无效字符,但对于我使用的文档类型无效。在处理提要之前,我通过sed find和replace运行了提要,并且能够解析大于2gb的字段。感谢大家的建议。XMLReader接口应该像SAX解析器一样按顺序处理大型文档,也就是说,它不(一定)将整个文档加载到内存中。谢谢。已经调整了内存。沃尔克也是对的。XMLReader以与SAX解析器类似的方式读取。如果其他方法都失败了,我将尝试使用SAX,但我宁愿不重写脚本。我正在编写一个脚本的独立副本,这可能会进一步说明这个问题,但我很确定XML或PHP脚本本身没有问题。只要文件小于1GB,它就可以按预期的方式运行,没有问题。即使更大,它也运行良好,只是无法读取所有xml。不过,感谢您的建议。“但我很确定这不是XML或PHP脚本本身的问题。”-只是为了确保:libxml_get_errors()这件事并不意味着scri有问题
    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;
        }
      }
    }