PHP循环 - 表达/功能导致严重延迟

时间:2011-04-01 11:44:54

标签: php performance loops

我想知道是否有人可以解决这个问题.PHP 5.3.0:)

我有一个循环,它抓取CSV文件的内容(大,200mb),处理数据,为mysql插入构建一堆变量,一旦循环完成并创建变量,我插入信息。

现在首先,mysql插件运行完美,没有延迟,一切都很好,但是它是LOOP本身有延迟,我最初使用fgetcsv()读取CSV文件,但与file_get_contents()相比,这有严重延迟 - 所以我切换到file_get_contents()。循环将在几秒钟内执行,直到我尝试添加一个函数(我还在循环内添加了表达式而没有函数来查看它是否有帮助)来创建一个包含每行的CSV数据的数组,这个是什么导致解析时间严重延迟! (基于这个200mb文件的差异大约是30秒,但我想根据csv文件的文件大小)

这是一些代码,所以你可以看到我在做什么:

$filename = "file.csv";
$content = file_get_contents($filename);    
$rows = explode("\n", $content);    
foreach ($rows as $data) {    
    $data = preg_replace("/^\"(.*)\"$/","$1",preg_split("/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/", trim($data))); //THIS IS THE CULPRIT CAUSING SLOW LOADING?!?
}

运行上面的循环,几乎不会立即执行:

$data = preg_replace("/^\"(.*)\"$/","$1",preg_split("/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/", trim($data)));

我也尝试过如下创建一个函数(在循环之外):

function csv_string_to_array($str) {
$expr="/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/";
$results=preg_split($expr,trim($str));
return preg_replace("/^\"(.*)\"$/","$1",$results);
}

并调用函数而不是一个衬垫:

$data = csv_string_to_array($data);

再次没有运气:(

任何帮助都会受到赞赏,我猜测fgetcsv函数基于它引起的延迟以非常类似的方式执行,循环并从数据行创建数组。

丹尼

5 个答案:

答案 0 :(得分:1)

正则表达式子表达式(以“(...)”为界)是问题所在。显示将这些添加到表达式可以大大降低其性能,这是微不足道的。我要尝试的第一件事是停止使用preg_replace()来简单地删除前导和尾随双引号(trim()将是更好的选择)并看看它有多大帮助。之后,您可能需要尝试使用非正则表达式来解析该行。

答案 1 :(得分:0)

我部分找到了一个解决方案,我发送批处理一次只循环1000行(php循环1000,直到到达文件末尾)。

我只是设置:

$data = preg_replace("/^\"(.*)\"$/","$1",preg_split("/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/", trim($data)));
1000行上的

,因此没有为导致问题的WHOLE文件设置它。

现在循环并在1-2秒内将1000行插入mysql数据库,我很满意。我已经设置脚本循环1000行,记住它的最后位置,然后循环到下一个1000直到它到达结束,它似乎工作正常!

答案 2 :(得分:0)

我说主要的罪魁祸首是preg_split()regexp的复杂性。 爆炸()可能会吃几秒钟。

$content = file_get_contents($filename);    
$rows = explode("\n", $content); 

可以替换为:

$rows = file ($filename); // returns an array

但是,我从ITroubs获得上述建议,fgetcsv()可能是一个更好的解决方案。

答案 3 :(得分:0)

顺便说一句,我认为你的函数没有按照你的想法做到:当你退出循环时,它实际上不会修改$ rows数组。要做到这一点,你需要更多的东西:

foreach ($rows as $key => $data) {
    $rows[$key]=preg_replace("/^\"(.*)\"$/","$1",preg_split("/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/", trim($data)));

答案 4 :(得分:0)

我建议使用fgetcsv来解析数据。看起来记忆可能是你最大的影响。因此,为了避免消耗200MB的RAM,您应该逐行解析,如下所示:

$fp = fopen($input, 'r');

while (($row = fgetcsv($fp, 0, ',', '"')) !== false) {
    $out = '"' . implode($row, '", "') . '"';  // quoted, comma-delimited output
    // perform work
}

或者:在preg中使用条件通常非常昂贵。使用explode()trim()及其$charlist参数处理这些行有时会更快。

另一种选择,如果您仍想使用preg,请添加S修饰符以尝试加速表达式。

  

http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
  <强>取值
  当一个模式将被多次使用时,值得花更多时间分析它以加快匹配所需的时间。如果设置了此修改器,则执行此额外分析。目前,研究模式仅对没有单个固定起始字符的非锚定模式有用。