古い話題:PHPが超大ファイルを読み込む


長年curdを深く耕してきたPHPerとして、メモリに注目することは不可能です.どうせapacheやfpmが手伝ってくれました.しかも、一度実行すると破棄され、メモリの問題はありません.
しかし、あいにく目立たない人がこれらのものを面接問題にしています.例えば、「phpで10 Gの超大きなファイルを読む」という意地悪な人が面接問題としてあなたに聞いています.もちろん、私と同じ普通のバカとして、あなたがこの問題を聞いた最初の瞬間は愚かで、2番目の瞬間は寝槽で、3番目の瞬間は結巴状態を維持しています.
「面接でロケットを作り、入社してネジを回す」.しかし、入ってすぐにネジを締めた人が「PHPが10 Gの超大型ファイルを読み取る」という見解を持っていれば、「ロケットを作る」のも遅かれ早かれのことだ.ここで「ねじをねじる」ためには、まず「10 Gファイルを読み込む」という問題を解決しなければなりません.
10 Gのファイルを読み込むには、まず10 Gのファイルが必要です.
... ...
実は、相対的に簡単なことで、私たちは勝手にnginxのログファイルを探して、たとえ10 KBでも、ファイル名がtestだと仮定します.log、それから「cat test.log>>test.log」を実行して、私の話を聞いて、少年、30秒ぐらいでctrl+Cを押すべきです.例えば、私のところ、感じてみてください.
202 MB、実験デモンストレーションとして、十分に面白いです.10 Gのファイルを作るのは難しいですか?
まずphpのfile関数で死を試みてみましょう

testとして保存php、コマンドラインの下で実行します.結果は下図のようになります.
PHPは最大でプロセスごとに128 MBのメモリしか割り当てられていませんが、202 MBのメモリを口にしますか?ではphpプロファイルを変更してみましょう
決して手加減しないで、このパラメータを1024 MBに変更して、上のphpスクリプトを再実行してください.
そして、大好きなfileをもう一度試してみましょう.get_contents()関数、結果は次の図です.
ファイルは一度にすべてメモリにロードされ、ファイルの各行をphp配列に保存しました.私の機械は10 Gメモリ+256 Gソリッドステートドライブで、この202 MBのファイルfile関数を一度にロードするのに0.67秒かかりました.file_get_contents関数は0.25秒(file_get_contentはfileよりずっと頼りになるように見えます)かかりましたが、肝心なのはコンフィギュレーションファイルを調整して202 MBのファイルを読み取ることができます.もし私たちの目の前に置かれているのが100 Gのファイルだったら?あるいは、システムが提供するphp構成は最大20 MBのメモリに与えられ、修正できませんか?
メモリの限られたマシンでメモリの数百倍のボリュームのファイルを読み込む方法に重点を置きます.次はmemory_Limitは16 Mに調整され、困難モードをオンにします.
202 MBのファイルは、割り当てられたメモリが16 MBであることを許可しているので、全体的な考え方も簡単です.少しずつ読むことです.毎回読む内容が16 MB未満であれば、問題はありません.まず、1文字1文字を感じてみましょう.ゲストはfgetc関数です.

次のように動作します.
16 Mメモリしか与えられていませんが、202 Mファイルをすべて読み取ることに成功しました.ただ、この実行速度は少し意味が悪くて、あまりよくありません.1文字ずつ読むことはできません.今回は1行ずつ読みます.

次のように動作します.
1行1行はやはり1文字よりずっと速いです.考えてみてください.システムが私たちに割り当てたメモリの上限は16 MBです.では、一度に一定の容量のデータを読み取ってみましょう.もっと速いのではないでしょうか.

コードを保存して、1本実行して、キックアスはキックアスになりました!!!メモリが限られている場合、私たちはまた時間を0.1秒に短縮しました!
それから私たちは問題をアップグレードすることを考えて、依然として上述の202 Mのファイルで、今回私たちは最後から5行の内容を読むことを要求して、この問題は少しキックアスに見えて、元のfreadなどで効果的ですが、いつも愚かな感じがします.だから、今また新しい関数を導入してこの問題を解決しなければなりません:ftellとfseek.ここでftellは、現在のファイル読取ポインタの位置を通知するために使用され、fseekは、ファイル読取ポインタの位置を手動で設定することができる.マニュアルでfseek関数を重点的に見ることをお勧めします.ここをクリックしてください.
 0 ){
  while( $ch != "
" ){ fseek( $fp, $pos, SEEK_END ); $ch = fgetc( $fp ); $pos--; } $ch = ''; $content .= fgets( $fp ); $line--; } echo $content; exit;

ここでtest 1.logファイルの内容は以下の通りです.
aa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccc
dddddddddddddddddddddddddddddddd
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
ffffffffffffffffffffffffffffffff
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccc
dddddddddddddddddddddddddddddddd
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
ffffffffffffffffffffffffffffffff
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccc
dddddddddddddddddddddddddddddddd
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
ffffffffffffffffffffffffffffffff
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccc
dddddddddddddddddddddddddddddddd
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
ffffffffffffffffffffffffffffffff
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccc
dddddddddddddddddddddddddddddddd
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
ffffffffffffffffffffffffffffffff
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccc
dddddddddddddddddddddddddddddddd
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
ffffffffffffffffffffffffffffffff
1111111111
2222222222

ファイルを保存して実行すると、次の図のようになります.