26MB の CSV ファイルがあります。大きくはありませんが、小さくもありません。file_get_contents でファイル全体を読み込んでいます。
$lines = explode(NEWLINE, file_get_contents($fname));
最初の行はヘッダーなので、explode(',', $line) を使用して、ヘッダー行をヘッダーの配列 ($hdrs) に変換します。そこから、ヘッダーの約 1/3 を取り除きます。それらは、保持している列から計算された値です。
$hdrs = explode(',', $lines[0]);
foreach ($hdrs AS $key=>$hdr) {
if (strpos($hdr, $srchStr) !== FALSE) { unset($hdrs[$key]; }
}
$hdrs = array_flip($hdrs);
そのため、各行をループして、その行を次のように処理します。
foreach ($lines AS $key=>$line) {
$data = explode(',', $line);
unset($lines[$key], $key, $line);
// This maps the array from keys 0, 1, 2, ... to the header fields
// It also eliminates any data fields not in the reduced $hdrs var
$data = remap_data($data, $hdrs);
// There are several line type and each line type has a subset of
// the now reduced data that is important
$type = determine_linetype($data);
// Depending on the line type I eliminate even more data
$data = further_reduce_data($data, $type);
// Here I ave the data to an array; this is AdWords account data so I have ad
// Campaigns at the top then Ad Groups then Keywords, ...
// I use a switch to determine where the data should go into the array
switch ($type) {
case Keyword:
$reducedData[$campaign][$adgroup]['KWs'][$kw] = $data;
break;
...
}
}
このループは期待どおりに機能し、希望どおりにデータを整理します。不要なデータも削除します。
私が使用しているテスト ファイルには 72000 行あります。スクリプトの約半分でメモリが不足します。スクリプトに使用できるメモリを 3 倍にすることができます。RAM があります。ただし、使用した行の設定を解除し、各行から半分以上のデータを削除すると、最終的には、完全にロードされた CSV ファイルよりも少ないメモリを使用して整理された配列になるという誤った印象を受けました。 . また、この行の後、使用されるメモリが約 4.5 MB ジャンプしますunset($lines[$key], $key, $line);
。これは意味がありませんが、何度もテストしました。
$data 配列のデータを減らすときは、var $output を作成し、保持したいすべての $data を $output に保存してから、元の $data を上書きする関数から $output を返します$data = reduce_data($data);
。
また、これらすべての文字列を int、double、date などにキャストしてみました。どちらの方法でも大きな違いはないようです。
また、次を使用して処理された1000行ごとに縮小ファイルを保存します
file_put_contents($fname, json_encode($reducedData));
私が言ったように、このスクリプトは途中で爆撃します。再編成されたデータの json でエンコードされたテキスト ファイルは、元のファイルよりも約 50% 大きくなります (データがはるかに少ないにもかかわらず)。ただし、これは依然として 40 MB のファイルを意味し、スクリプトは 120 MB を超えるメモリを使用するとメモリ不足になります。(ファイルを一度に 1 行ずつ読み取ることはできますが、50% ではなく 75% を爆破します。) そして、json でエンコードされたファイルを読み取ると、結果の配列がかなり大きくなると思います。
今のところ、スクリプトが使用できるメモリを増やすことができます。しかし、なぜこれが起こっているのかについてPHPの神々が何を言わなければならなかったのか知りたかった. 配列は本当にそんなに多くのオーバーヘッドを運ぶのでしょうか? 減らす方法はありますか?従うべきベストプラクティスはありますか?