PHP正则表达式优化

时间:2017-12-09 18:36:10

标签: php regex optimization

我正在尝试优化PHP正则表达式,并正在寻求精彩Stack Overflow社区的指导。

我试图在HTML块中捕获预定义的匹配项,例如:

##test##

##!test2##

##test3|id=5##

将运行的示例文本是:

  

Lorem ipsum dolor sit amet,## test ## consectetur adipiscing elit。 Pellentesque id congue massa。 Curabitur ## test3 | id = 5 ## egestas ullamcorper sollicitudin。 Mauris venenatis sed metus vitae pharetra。

到目前为止我有两种选择。从优化的角度来看最好的想法是什么?

选项1

~##(!?)(test|test2|test3)(|\S+?)##~s

选项2

~\##(\S+)##~s

对于示例"!"中的\##!test2##,它旨在标记项目在处理时的特殊行为。这可以移动为##test3|force=true&id=5##之类的属性。如果是这种情况,那就是:

选项3

~##(test|test2|test3)(|\S+?)##~s

我们关注的最重要因素是性能和优化。

提前感谢您的帮助和见解!

2 个答案:

答案 0 :(得分:2)

正如其他人所提到的,你需要为表达式计时。 Python具有精彩的timeit模块,而对于PHP,您需要提出自己的解决方案:

<?php

$string = <<<DATA
Lorem ipsum dolor sit amet, ##test## consectetur adipiscing elit. Pellentesque id congue massa. Curabitur ##test3|id=5## egestas ullamcorper sollicitudin. Mauris venenatis sed metus vitae pharetra.
DATA;

function timeit($regex, $string, $number) {
    $start = microtime(true);

    for($i=0;$i<$number;$i++) {
        preg_match_all($regex, $string, $matches);
    }

    return microtime(true) - $start;
}

$expressions = ['~##(!?)(test|test2|test3)(|\S+?)##~s', '~\##(\S+)##~s', '~##(test|test2|test3)(|\S+?)##~s'];
$cnt = 1;
foreach ($expressions as $expression) {
    echo "Expression " . $cnt . " took " . timeit($expression, $string, 10**5) . "\n";
    $cnt++;
}
?>

<小时/> 在我的计算机上运行(每次迭代100k)会产生

Expression 1 took 0.45759010314941
Expression 2 took 0.34269499778748
Expression 3 took 0.40994691848755

显然,您可以使用其他字符串和更多迭代,但这会给您一个大致的想法。

答案 1 :(得分:0)

如果您需要根据字符出现来剖析和处理匹配的子字符串,那么在正则表达式步骤中分离组件似乎最合乎逻辑 - 在准确性和易于处理之后关注模式优化。

我的模式包含三个捕获组,只有中间一个需要一个正长度的字符串。否定捕获组用于模式效率。我假设您的子字符串不包含用于分隔子字符串的#。如果它们可能包含#,那么请更新您的问题,我会更新我的答案。

Pattern Demo

模式说明:

/          // pattern delimiter
##         // match leading substring delimiter
(!)?       // optionally capture: an exclamation mark
([^#|]+)   // greedily capture: one or more non-hash, non-pipe characters
\|?        // optionally match: a pipe
([^#]+)?   // optionally capture: one or more non-hash characters
##         // match trailing substring delimiter
/          // pattern delimiter

代码:(Demo

$string='Lorem ipsum dolor sit amet, ##test## consectetur adipiscing elit. Pellentesque id congue massa. Curabitur ##test3|id=5## egestas ullamcorper sollicitudin. Mauris venenatis sed metus ##!test2## vitae pharetra.';

$result=preg_replace_callback(
    '/##(!)?([^#|]+)\|?([^#]+)?##/',
    function($m){
        echo '$m = ';
        var_export($m);
        echo "\n";
        // execute custom processing:
        if(isset($m[1][0])){  //check first character of element (element will always be set because $m[2] will always be set)
            echo "exclamation found\n";
        }
        // $m[2] is required (will always be set)
        if(isset($m[3])){  // will only be set if there is a positive-length string in it
            echo "post-pipe substring found\n";
        }
        echo "\n---\n";
        return '[some replacement text]';
    },$string);

var_export($result);

输出:

$m = array (
  0 => '##test##',
  1 => '',
  2 => 'test',
)

---
$m = array (
  0 => '##test3|id=5##',
  1 => '',
  2 => 'test3',
  3 => 'id=5',
)
post-pipe substring found

---
$m = array (
  0 => '##!test2##',
  1 => '!',
  2 => 'test2',
)
exclamation found

---
'Lorem ipsum dolor sit amet, [some replacement text] consectetur adipiscing elit. Pellentesque id congue massa. Curabitur [some replacement text] egestas ullamcorper sollicitudin. Mauris venenatis sed metus [some replacement text] vitae pharetra.'

如果您正在执行自定义替换过程,此方法将“优化”您的字符串处理。