我有一个 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 行。脚本执行到一半时内存不足。我可以将可用于脚本的内存增加三倍,我有 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 行,但它会轰炸 75% 而不是 50%。)我假设当我读入 json 编码文件时,生成的数组会非常大。
现在我可以增加允许脚本使用的内存。但是,我想知道 PHP 之神对为什么会发生这种情况有什么看法。数组真的会带来那么多开销吗?有没有办法减少它?我应该遵循的任何最佳实践?