【问题标题】:PHP file writing optimizationPHP文件写入优化
【发布时间】:2009-11-13 16:02:58
【问题描述】:

编辑:此问题末尾的优化结果!

嗨,我有以下代码首先扫描特定文件夹中的文件,然后逐行读取每个文件,并在多次“if...else if”之后将新修改的文​​件写入另一个名为 name 的文件夹是打开的时候。

问题是逐行编写文件似乎非常慢。默认的 60 秒限制仅能容纳 25 个左右的文件。文件大小从 10k 到 350k 不等。

任何优化代码以使其运行得更快的方法。逐行读取是否更好,将每一行放入一个数组中,然后将整个数组写入一个新的文本文件(而不是逐行读取/写入)。如果是,在实践中是如何做到的。

提前致谢 ----- 代码如下 -----

<?php

function scandir_recursive($path)    {
...
...
}



$fileselection = scandir_recursive('HH_new');
foreach ($fileselection as $extractedArray) {
$tableName = basename($extractedArray); // Table name
$fileLines=file($extractedArray);
    foreach ($fileLines as $line) {
            if(preg_match('/\(all-in\)/i' , $line)) {
                $line = stristr($line, ' (all-in)', true) .', and is all in';
                $allin = ', and is all in';
            }
            else {
                $allin = '';
            }
            if(preg_match('/posts the small blind of \$[\d\.]+/i' , $line)) {
                $player = stristr($line, ' posts ', true);
                $betValue = substr(stristr($line, '$'), 1);
                $bettingMatrix[$player]['betTotal'] = $betValue;
            }
            else if(preg_match('/posts the big blind of \$[\d\.]+/i' , $line)) {
                $player = stristr($line, ' posts ', true);
                $betValue = substr(stristr($line, '$'), 1);
                $bettingMatrix[$player]['betTotal'] = $betValue;
            }
            else if(preg_match('/\S+ raises /i' , $line)) {
                $player = stristr($line, ' raises ', true);
                $betValue = substr(strstr($line, '$'), 1);
                $bettingMatrix[$player]['betTotal'] = $betValue; //total bet this hand (shortcut)
            }
            else if(preg_match('/\S+ bets /i' , $line)) {
                $player = stristr($line, ' bets ', true);
                $betValue = substr(strstr($line, '$'), 1);
                $bettingMatrix[$player]['betTotal'] = $betValue; //total bet this hand (shortcut)
            }
            else if(preg_match('/\S+ calls /i' , $line)) {
                $player = stristr($line, ' calls ', true);
                $betValue = substr(stristr($line, '$'), 1);
                $callValue = $betValue - $bettingMatrix[$player]['betTotal']; //actual amount called
                $bettingMatrix[$player]['betTotal'] = $betValue;
                $line = stristr($line, '$', true)."\$".$callValue.$allin;
                $allin = '';
            }
            else if(preg_match('/(\*\*\* (Flop|Turn|River))|(Full Tilt Poker)/i' , $line)) {
                unset($bettingMatrix); //zero $betValue
            }
            else if(preg_match('/\*\*\* FLOP \*\*\*/i' , $line)) {
                $flop = substr(stristr($line, '['), 0, -2);
                $line = '*** FLOP *** '. $flop;
            }
            else if(preg_match('/\*\*\* TURN \*\*\*/i' , $line)) {
                $turn = substr(stristr($line, '['), 0, -2);
                $line = '*** TURN *** '. $flop .' '. $turn;
            }
            else if(preg_match('/\*\*\* RIVER \*\*\*/i' , $line)) {
                $river = substr(stristr($line, '['), 0, -2);
                $line = '*** RIVER *** '. substr($flop, 0, -1) .' '. substr($turn, 1) .' '. $river;
            }
            else {
            }
        $ourFileHandle = fopen("HH_newest/".$tableName.".txt", 'a') or die("can't open file");
        fwrite($ourFileHandle, $line);
        fclose($ourFileHandle);
    }
}
?>

编辑: 这是根据这里的每个人给我的提示重写代码后的非常有趣的结果。

60 个文本文件,共 5.8MB

经过所有优化(更改 preg->strpos/strstr & $handle before loop):4 sec.

如上所述,但改变了 strpos/strstr -> stripos/stristr: 8 sec.

如上所述,但更改了 stripos/stristr -> preg: 12 sec.

如上所述,但在循环内更改了 fopen:45/60 个文件在 180 秒运行限制后

这是完整的脚本:

$fileselection = scandir_recursive('HH_new');
foreach ($fileselection as $extractedArray) {
    $tableName = basename($extractedArray); // Table name
    $handle         = fopen($extractedArray, 'r');
    $ourFileHandle  = fopen("HH_newest/".$tableName.".txt", 'a') or die("can't open file");
    while ($line = fgets($handle)) {
            if (FALSE !== strpos($line, '(all-in)')) {
                $line = strstr($line, ' (all-in)', true) .", and is all in\r\n";
                $allin = ', and is all in';
            } else {
                $allin = '';
            }
            if (FALSE !== strpos($line, ' posts the small blind of $')) {
                $player = strstr($line, ' posts ', true);
                $betValue = substr(strstr($line, '$'), 1);
                $bettingMatrix[$player]['betTotal'] = $betValue;
            }
            else if (FALSE !== strpos($line, ' posts the big blind of $')) {
                $player = strstr($line, ' posts ', true);
                $betValue = substr(strstr($line, '$'), 1);
                $bettingMatrix[$player]['betTotal'] = $betValue;
            }
            else if (FALSE !== strpos($line, ' posts $')) {
                $player = strstr($line, ' posts ', true);
                $betValue = substr(strstr($line, '$'), 1);
                $bettingMatrix[$player]['betTotal'] += $betValue;
            }
            else if (FALSE !== strpos($line, ' raises to $')) {
                $player = strstr($line, ' raises ', true);
                $betValue = substr(strstr($line, '$'), 1);
                $betMade = $betValue - $bettingMatrix[$player]['betTotal']; //actual amount raised by
                $bettingMatrix[$player]['betTotal'] = $betValue; //$line contains total bet this hand (shortcut)
            }
            else if (FALSE !== strpos($line, ' bets $')) {
                $player = strstr($line, ' bets ', true);
                $betValue = substr(strstr($line, '$'), 1);
                $betMade = $betValue - $bettingMatrix[$player]['betTotal']; //actual amount raised by
                $bettingMatrix[$player]['betTotal'] = $betValue; //$line contains total bet this hand (shortcut)
            }
            else if (FALSE !== strpos($line, ' calls $')) {
                $player = strstr($line, ' calls ', true);
                $betValue = substr(strstr($line, '$'), 1);
                $callValue = $betValue - $bettingMatrix[$player]['betTotal']; //actual amount called
                $bettingMatrix[$player]['betTotal'] = $betValue;
                $line = strstr($line, '$', true)."\$".$callValue.$allin. "\r\n";
                $allin = '';
            }
            else if (FALSE !== strpos($line, '*** FLOP ***')) {
                $flop = substr(strstr($line, '['), 0, -2);
                unset($bettingMatrix); //zero $betValue
            }
            else if (FALSE !== strpos($line, '*** TURN ***')) {
                $turn = substr(strstr($line, '['), 0, -2);
                $line = '*** TURN *** '.$flop.' '.$turn."\r\n";
                unset($bettingMatrix); //zero $betValue
            }
            else if (FALSE !== strpos($line, '*** RIVER ***')) {
                $river = substr(strstr($line, '['), 0, -2);
                $line = '*** RIVER *** '. substr($flop, 0, -1) .' '. substr($turn, 1) .' '. $river."\r\n";
                unset($bettingMatrix); //zero $betValue
            }
            else if (FALSE !== strpos($line, 'Full Tilt Poker')) {
                unset($bettingMatrix); //zero $betValue
            }
            else {
            }
        fwrite($ourFileHandle, $line);
    }
    fclose($handle);
    fclose($ourFileHandle);
}

【问题讨论】:

  • 看到非正则表达式匹配的性能增益与较少文件 I/O 的增益相比会很有趣。
  • 我会在回到“我的实验室”时发布一些结果。
  • 我这里的代码是基于 MalphasWats 的代码。可以从下面的链接中找到我最初的,非常混乱的问题。如果你看一下,只需要调用调用值。 stackoverflow.com/questions/1665927/…

标签: php optimization file pcre


【解决方案1】:

我认为这是因为你在循环中打开/关闭文件,尝试在 foreach 之前移动 fopen() 并在它之后移动 fclose

【讨论】:

  • 那么它只会将每个文件的最后一行写入一个新文件,不是吗?
  • 我同意你绝对不想做这么多次 I/O。将每个修改的行附加到新字符串然后写出新字符串一次可能会更快。
  • 只要你将 fwrite() 留在循环中,它只会让文件保持打开状态并继续向其中写入每一行。
【解决方案2】:

我怀疑文件写入是这里的性能问题。您正在所有内容上运行个正则表达式!

使用strpos 之类的字符串方法来查找子字符串可能会加快速度。

【讨论】:

  • 如何用 strpos() 替换我的“preg_match('/\S+ calls /i', $line)”? “\S+”有替代品吗?
  • 如果前 9 个都不匹配,则只有 10 个。鉴于此,重新安排 if-elseifs 以按概率顺序进行测试可能会在一定程度上提高性能。
【解决方案3】:

如果您可以将正则表达式更改为 strpos() 或类似的形式(不区分大小写的 stripos()),那么取消正则表达式将给您带来最大的性能提升 - 您应该会注意到速度的提升。

测试需要'!== false',因为找到的字符串可能在位置0。例如,您的第一个测试用例可能是():

if(stripos($line, '(all-in)') !== false) {
    //generate output
}

您还可能会发现使用 fgets() 而不是一次读取整个文件可能会给您带来一些性能提升(但这更多是内存问题)。而且正如其他人所说,只在循环中写入文件,不要打开和关闭它。

【讨论】:

  • 我一直想知道为什么它必须是“不是假的”而不是“真的”?它是否提供更好的性能或可靠性?
  • @mika - strpos() 的 PHP 手册页突出地解释了这一点。
  • @minka 我添加了简短的解释,并将函数名称链接到手册页。
【解决方案4】:

这是你的代码,有一些微小的变化应该会有所帮助

  1. file() 切换到fgets()。这将一次仅将一行加载到内存中,而不是文件中的每一行。
  2. 在适用的情况下将您对 preg_match() 的呼叫更改为 stripos()。应该会快一点
  3. $ourFileHandle 的打开/关闭移动到外循环中。这将显着减少对文件系统的统计调用次数,并应大大加快速度。

可能还有很多其他优化可以在那个可怕的 if..else 中进行,但我会把这些留给另一个 SOer(或你)

$fileselection = scandir_recursive('HH_new');
foreach ($fileselection as $extractedArray)
{ 
  $tableName     = basename( $extractedArray ); // Table name
  $handle        = fopen( $extractedArray, 'r' );
  $ourFileHandle = fopen("HH_newest/".$tableName.".txt", 'a') or die("can't open file");

  while ( $line = fgets( $handle ) )
  {
    if ( false !== stripos( $line, '(all-in)' ) )
    {
      $line = stristr($line, ' (all-in)', true) .', and is all in';
      $allin = ', and is all in';
    } else {
      $allin = '';
    }
    if ( preg_match('/posts the small blind of \$[\d\.]+/i' , $line ) )
    {
            $player = stristr($line, ' posts ', true);
            $betValue = substr(stristr($line, '$'), 1);
            $bettingMatrix[$player]['betTotal'] = $betValue;
    }
    else if(preg_match('/posts the big blind of \$[\d\.]+/i' , $line)) {
            $player = stristr($line, ' posts ', true);
            $betValue = substr(stristr($line, '$'), 1);
            $bettingMatrix[$player]['betTotal'] = $betValue;
    }
    else if(preg_match('/\S+ raises /i' , $line)) {
            $player = stristr($line, ' raises ', true);
            $betValue = substr(strstr($line, '$'), 1);
            $bettingMatrix[$player]['betTotal'] = $betValue; //total bet this hand (shortcut)
    }
    else if(preg_match('/\S+ bets /i' , $line)) {
            $player = stristr($line, ' bets ', true);
            $betValue = substr(strstr($line, '$'), 1);
            $bettingMatrix[$player]['betTotal'] = $betValue; //total bet this hand (shortcut)
    }
    else if(preg_match('/\S+ calls /i' , $line)) {
            $player = stristr($line, ' calls ', true);
            $betValue = substr(stristr($line, '$'), 1);
            $callValue = $betValue - $bettingMatrix[$player]['betTotal']; //actual amount called
            $bettingMatrix[$player]['betTotal'] = $betValue;
            $line = stristr($line, '$', true)."\$".$callValue.$allin;
            $allin = '';
    }
    else if(preg_match('/(\*\*\* (Flop|Turn|River))|(Full Tilt Poker)/i' , $line)) {
            unset($bettingMatrix); //zero $betValue
    }
    else if ( FALSE !== stripos( $line, '*** FLOP ***' ) )
    {
            $flop = substr(stristr($line, '['), 0, -2);
            $line = '*** FLOP *** '. $flop;
    }
    else if ( FALSE !== stripos( $line, '*** TURN ***' ) )
    {
            $turn = substr(stristr($line, '['), 0, -2);
            $line = '*** TURN *** '. $flop .' '. $turn;
    }
    else if ( FALSE !== stripos( $line, '*** RIVER ***' ) )
    {
            $river = substr(stristr($line, '['), 0, -2);
            $line = '*** RIVER *** '. substr($flop, 0, -1) .' '. substr($turn, 1) .' '. $river;
    }
    else {
    }
    fwrite($ourFileHandle, $line);
  }
  fclose( $handle );
  fclose( $ourFileHandle );
}

【讨论】:

  • 谢谢..我将根据您的代码重写我的代码,并保留每个人在这里告诉的其他想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 2010-12-24
相关资源
最近更新 更多