web-dev-qa-db-ja.com

PHP

fopenで非常に適度なサイズのファイルを読み取ろうとすると、PHPが失敗します。 A 6 meg fileを使用すると狭くなりますが、100kの周りの小さいファイルは問題ありません。 20ギガを超えるファイルやとんでもないファイルを読み取るためにPHP-D_FILE_OFFSET_BITS=64フラグで再コンパイルする必要がある場合があることを読みましたが、6 MBのファイルで問題はないはずです。 ?最終的には、100 MB程度のファイルを読み込みたいと思います。小さいファイルでできるので、それらを開いて、fgetsで1行ずつ読み取ることができればいいでしょう。

PHPの非常に大きなファイルの読み取りと操作を行うためのトリック/ソリューションは何ですか?

更新:6 megファイルで失敗する単純なコードブロックの例を次に示します-PHPはエラーをスローしないようで、単にfalseを返します。たぶん私は何か非常に馬鹿げたことをしていますか?

$rawfile = "mediumfile.csv";

if($file = fopen($rawfile, "r")){  
  fclose($file);
} else {
  echo "fail!";
}

別の更新:皆さんの助けに感謝します、それは信じられないほどおかしなものであることが判明しました-アクセス許可の問題です。私の小さなファイルには、大きなファイルにはないのに、どうしても読み取り権限がありました。どー!

25
user5564

スクリプトのタイムアウト設定ではなく、失敗しているのはfopenですか?デフォルトは通常約30秒程度であり、ファイルの読み取りにそれよりも時間がかかる場合は、トリップする可能性があります。

考慮すべきもう1つのことは、スクリプトのメモリ制限である可能性があります。ファイルを配列に読み込むと、これが失敗する可能性があるため、エラーログでメモリ警告を確認してください。

上記のいずれも問題でない場合は、 fgets を使用してファイルを1行ずつ読み取り、処理を進めてみてください。

$handle = fopen("/tmp/uploadfile.txt", "r") or die("Couldn't get handle");
if ($handle) {
    while (!feof($handle)) {
        $buffer = fgets($handle, 4096);
        // Process buffer here..
    }
    fclose($handle);
}

編集

PHPはエラーをスローしないようで、単にfalseを返します。

$rawfileへのパスは、スクリプトが実行されている場所に対して相対的ですか?ファイル名に絶対パスを設定してみてください。

47
ConroyP

1.3GBファイルと9.5GBファイルで2つのテストを行いました。

1.3 GB

fopen()を使用する

このプロセスでは、計算に15555 msを使用しました。

システムコールに169ミリ秒かかりました。

file()を使用する

このプロセスは、計算に6983 msを使用しました。

システムコールに4469ミリ秒かかりました。

9.5 GB

fopen()を使用する

このプロセスでは、計算に113559 msを使用しました。

システムコールに2532ミリ秒かかりました。

file()を使用する

このプロセスでは、計算に8221 msを使用しました。

システムコールに7998ミリ秒かかりました。

file()の方が速いようです。

7
Al-Punk

fgets()関数は、テキストファイルが20 MBを通過し、解析速度が大幅に低下するまで問題ありません。

file_ get_contents()関数は、40 MBytesまでは良好な結果を、100 MBytesまでは許容可能な結果を​​提供しますが、-file_get_contents()はファイル全体をメモリにロードするなので、スケーラブルではありません。

file()関数は、テキストの各行を含む配列を作成し、この配列がメモリに格納され、使用されるメモリがさらに大きくなるため、テキストの大きなファイルでは悲惨です。
実際、200 MBのファイルを解析できたのは、_memory_limit_を2 GBに設定した場合のみでした。これは、解析しようとした1 GB以上のファイルには不適切でした。

1 GBを超えるファイルを解析する必要があり、解析時間が15秒を超えていて、ファイル全体をメモリにロードしたくない場合は、別の方法を見つける必要があります。

私の解決策は任意の小さなチャンクでデータを解析するでした。コードは次のとおりです。

_$filesize = get_file_size($file);
$fp = @fopen($file, "r");
$chunk_size = (1<<24); // 16MB arbitrary
$position = 0;

// if handle $fp to file was created, go ahead
if ($fp) {
   while(!feof($fp)){
      // move pointer to $position in file
      fseek($fp, $position);

      // take a slice of $chunk_size bytes
      $chunk = fread($fp,$chunk_size);

      // searching the end of last full text line
      $last_lf_pos = strrpos($chunk, "\n");

      // $buffer will contain full lines of text
      // starting from $position to $last_lf_pos
      $buffer = mb_substr($chunk,0,$last_lf_pos);

      ////////////////////////////////////////////////////
      //// ... DO SOMETHING WITH THIS BUFFER HERE ... ////
      ////////////////////////////////////////////////////

      // Move $position
      $position += $last_lf_pos;

      // if remaining is less than $chunk_size, make $chunk_size equal remaining
      if(($position+$chunk_size) > $filesize) $chunk_size = $filesize-$position;
      $buffer = NULL;
   }
   fclose($fp);
}
_

使用されるメモリは_$chunk_size_のみであり、速度はfile_ get_contents()で取得されるものよりもわずかに遅くなります。 PHPグループは、解析機能を最適化するために私のアプローチを使用する必要があります。

*)get_file_size()関数を検索 here

1
Tinel Barb

ファイルを出力するだけの場合は、readfile関数を使用してみてください。

そうでない場合-アプリケーションの設計について考える必要があるかもしれませんが、Webリクエストでそのような大きなファイルを開くのはなぜですか?

1
Fionn

私はfopenを使用して、phpスクリプトをビデオストリーミングサーバーとして使用し、ストリーミング用のビデオファイルを開きました。サイズが50/60 MBを超えるファイルでも問題ありませんでした。

1
Enrico Murru

私にとって、fopen()は1MBを超えるファイルで非常に遅くなり、file()ははるかに高速です。

一度に100行ずつ読み取ってバッチ挿入を作成しようとすると、fopen()は37秒かかります。vsfile()は4秒かかります。 file()に組み込まれているstring->arrayステップである必要があります

すべてのファイル処理オプションを試して、どちらがアプリケーションで最適に機能するかを確認します。

0
RightClick

問題がメモリ制限に達したことが原因である場合は、それをより高い値に設定してみてください(これは、phpの構成によっては機能するかどうかにかかわらず)。

これにより、メモリ制限が12 Mbに設定されます。

ini\_set("memory_limit","12M");
0