4

XMLReader が処理できる最大ファイル サイズはありますか?

約 3GB の XML フィードを処理しようとしています。スクリプトは正常に実行され、実行後にデータベースに正常にロードされるため、PHP エラーはまったくありません。

このスクリプトは、1 GB 以下の小さなテスト フィードでも正常に動作します。ただし、より大きなフィードを処理する場合、スクリプトは約 1GB 後に XML ファイルの読み取りを停止し、残りのスクリプトの実行を続行します。

誰かが同様の問題を経験しましたか? もしそうなら、どのようにそれを回避しましたか?

前もって感謝します。

4

6 に答える 6

2

私は最近同じような問題を抱えていたので、私の経験を共有することを考えました.

PHP が 64 ビットのファイル サイズ/オフセットをサポートしてコンパイルされたか、32 ビットのみでコンパイルされたかにかかわらず、問題は PHP のコンパイル方法にあるようです。

32 ビットでは、4 GB のデータしかアドレス指定できません。少しわかりにくいですが、良い説明がここにあります: 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 ファイルを要素全体に分割することです。残念ながら、それほど速くはありません。

これは 1 回だけ行う必要があり、私のニーズには合っていましたが、繰り返し使用することはお勧めしません。分割後、約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。これは、ツリー全体を解析する必要のないストリーム指向のパーサーです。メモリ効率が大幅に向上しますが、プログラミングが少し難しくなります。

OS によっては、割り当てることができる RAM チャンクに 2 GB の制限がある場合もあります。32ビットOSで実行している場合、非常に可能です。

于 2010-08-06T15:05:37.110 に答える
1

一般に、PHP には最大ファイル サイズがあることに注意してください。PHP では、符号なし整数または長整数を使用できません。つまり、整数の上限は 2^31 (または 64 ビット システムの場合は 2^63) です。これは重要です。なぜなら、PHP はファイル ポインター (ファイル内での読み取り時の位置) に整数を使用するためです。つまり、サイズが 2^31 バイトを超えるファイルは処理できないということです。

ただし、これは 1 ギガバイトを超える必要があります。2 ギガバイトで問題が発生しました (予想どおり、2^31 は約 20 億です)。

于 2010-08-11T21:16:07.927 に答える
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 に答える
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# テスト アプリケーションは、7.5 分ではなく 41 秒しかかかりませんでした。そして、私の遅いハードドライブが、この場合の制限要因の 1 つだった可能性があります。

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 に答える