PHP では、ファイルを読み取るときに、file や file_get_contents などの関数を使用するのが最も速い方法で、数行の簡単なコードで必要な関数を美しく完成させることができます。
しかし、操作されるファイルが比較的大きなファイルである場合、これらの機能は不十分である可能性があります。以下では、PHP が大きなファイルを読み取るための一般的な操作方法を説明するための要件から始めます。
要件は、約 500 万行を含む 8 億のログ ファイルがあり、PHP を使用して最後の数行の内容を返すことです。
実装方法
file 関数はすべての内容を一度にメモリに読み込みます。PHP では、この値が設定されている場合、使用できる最大メモリは 16M に制限されます。 -1 に設定すると、メモリ使用量は制限されません。
次は、file を使用してこのファイルの最後の行を抽出するコードです:
<!--?php</span--><br /> ini_set('memory_limit', '-1');<br /> $file = 'access.log';<br /> $data = file($file);<br /> $line = $data[count($data) - 1];<br /> echo $line;</p> <p>?>
コード全体の実行には 116.9613 (秒) かかります。私のマシンには 2G のメモリがあり、F5 キーを押して実行すると、システムがグレーになり、ほぼ 20 分後に回復します。このような大きなファイルをメモリに直接読み込むと、非常に深刻な結果が生じることがわかります。したがって、memory_limit をあまり高く調整することはできません。そうでない場合は、コンピューター室に電話してマシンをリセットするように依頼するしかありません。
Linux コマンド ラインでは、tail -n 10 access.log を直接使用して、ログ ファイルの最後の数行を簡単に表示できます。PHP を使用して、tail コマンドを直接呼び出すことができます。実行 PHP コードは次のとおりです。 :
<!--?php</span--><br /> $file = 'access.log';<br /> $file = escapeshellarg($file); // 对命令行参数进行安全转义<br /> $line = `tail -n 1 $file`;<br /> echo $line;</p> <p> </p> <p>?>
コード全体の実行には 0.0034 (秒) かかります
この方法は最も一般的な方法であり、ファイルの内容をすべて読み取る必要はなく、ポインターを介して直接操作するため、効率が非常に優れています。
fseek を使用してファイルを操作する場合、さまざまな方法があり、効率が若干異なる場合があります。一般的に使用される方法は次の 2 つです。
方法 1
最初に fseek を通じてファイルの最後の EOF を見つけ、次に最後の行の開始位置を見つけ、この行のデータを取得し、次に次の行の開始位置を見つけて、この行の位置を取得します。 $num 行目で見つかるまで続きます。
#実装コードは以下の通りです
<!--?php</span--><br /> $fp = fopen($file, "r");<br /> $line = 10;<br /> $pos = -2;<br /> $t = " ";<br /> $data = "";<br /> while ($line > 0)<br /> {<br /> while ($t != "\n")<br /> {<br /> fseek($fp, $pos, SEEK_END);<br /> $t = fgetc($fp);<br /> $pos--;<br /> }<br /> $t = " ";<br /> $data .= fgets($fp);<br /> $line--;<br /> }<br /> fclose($fp);<br /> echo $data</p> <p> </p> <p>?>
コード全体の実行には 0.0095 (秒) かかります
方法 2
ファイルの末尾から読み取るには fseek を使用しますが、今回は少しずつ読み取るのではなく、データを部分的に読み取るたびに、読み取られたデータが buf に配置されます。 、改行文字の数 (n) を使用して、最後の $num 行のデータが読み取られたかどうかを判断します。
#実装コードは以下の通りです
<!--?php</span--><br /> $fp = fopen($file, "r");<br /> $num = 10;<br /> $chunk = 4096;<br /> $fs = sprintf("%u", filesize($file));<br /> $max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file);<br /> for ($len = 0; $len < $max; $len += $chunk)<br /> {<br /> $seekSize = ($max - $len > $chunk) ? $chunk : $max - $len;<br /> fseek($fp, ($len + $seekSize) * -1, SEEK_END);<br /> $readData = fread($fp, $seekSize) . $readData;<br /> if (substr_count($readData, "\n") >= $num + 1)<br /> {<br /> preg_match("!(.*?\n){" . ($num) . "}$!", $readData, $match);<br /> $data = $match[0];<br /> break;<br /> }<br /> }<br /> fclose($fp);<br /> echo $data;</p> <p> </p> <p>?>
コード全体の実行には 0.0009 秒かかります。
方法 3
<!--?php</span--><br /> function tail($fp, $n, $base = 5)<br /> {<br /> assert($n > 0);<br /> $pos = $n + 1;<br /> $lines = array();<br /> while (count($lines) <= $n)<br /> {<br /> try<br /> {<br /> fseek($fp, -$pos, SEEK_END);<br /> }<br /> catch (Exception $e)<br /> {<br /> fseek(0);<br /> break;<br /> }<br /> $pos *= $base;<br /> while (!feof($fp))<br /> {<br /> array_unshift($lines, fgets($fp));<br /> }<br /> }<br /> <br /> return array_slice($lines, 0, $n);<br /> }<br /> <br /> var_dump(tail(fopen("access.log", "r+"), 10));</p> <p> </p> <p>?>
コード全体の実行には 0.0003(s) かかります