Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/230.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP中标记化CSS的性能_Php_Performance_Parsing_Token_Lexer - Fatal编程技术网

PHP中标记化CSS的性能

PHP中标记化CSS的性能,php,performance,parsing,token,lexer,Php,Performance,Parsing,Token,Lexer,这是一个从未编写过解析器/词法分析器的人提出的noob问题 我正在用PHP为CSS编写一个标记器/解析器(请不要重复“OMG,为什么用PHP?”)。语法由W3C简洁明了地写下来 这是一个包含21个可能标记的列表,所有标记(除了两个)都不能表示为静态字符串 我目前的方法是反复遍历包含21个模式的数组,执行if(preg_match())并逐个减少源字符串匹配。从原则上讲,这真的很有效。然而,对于1000行CSS字符串,这需要2到8秒的时间,这对我的项目来说太多了 现在我在想其他解析器如何在几秒钟内

这是一个从未编写过解析器/词法分析器的人提出的noob问题

我正在用PHP为CSS编写一个标记器/解析器(请不要重复“OMG,为什么用PHP?”)。语法由W3C简洁明了地写下来

这是一个包含21个可能标记的列表,所有标记(除了两个)都不能表示为静态字符串

我目前的方法是反复遍历包含21个模式的数组,执行
if(preg_match())
并逐个减少源字符串匹配。从原则上讲,这真的很有效。然而,对于1000行CSS字符串,这需要2到8秒的时间,这对我的项目来说太多了

现在我在想其他解析器如何在几秒钟内标记和解析CSS。好的,C总是比PHP快,但不管怎样,有没有明显的D'Oh!我掉进了什么地方

我做了一些优化,比如检查'@'、'#'或'''作为剩余字符串的第一个字符,然后只应用相关的regexp,但这并没有带来任何性能提升

到目前为止,我的代码(片段):


我要做的第一件事是去掉
preg\u match()
。基本的字符串函数,如
strpos()
要快得多,但我认为您甚至不需要它。看起来您正在使用
preg\u match()在字符串前面寻找特定的标记
,然后只需将该字符串的前端长度作为子字符串。您可以使用简单的
substr()
来轻松完成此操作,如下所示:

foreach ($TOKENS as $t => $p)
{
    $front = substr($string,0,strlen($p));
    $len = strlen($p);  //this could be pre-stored in $TOKENS
    if ($front == $p) {
        $stream[] = array($t, $string);
        $string = substr($string, $len);
        // Yay! We found one that matches!
        continue 2;
    }
}
通过预先计算所有令牌的长度并将其存储在
$tokens
数组中,您可以进一步优化此功能,这样您就不必一直调用
strlen()
。如果您将
$tokens
按长度分组,则可以减少
substr()的数量
还可以进一步调用,因为您可以对每个令牌长度只分析一次当前字符串的
substr($string)
,并在转到下一组令牌之前遍历该长度的所有令牌。

使用a。

可能更快(但内存不太友好)方法是一次对整个流进行标记化,使用一个大的regexp和每个标记的替代项,如

 preg_match_all('/
       (...string...)
       |
       (@ident)
       |
       (#ident)
       ...etc
   /x', $stream, $tokens);

 foreach($tokens as $token)...parse

不要使用regexp,逐个字符扫描

$tokens = array();
$string = "...code...";
$length = strlen($string);
$i = 0;
while ($i < $length) {
  $buf = '';
  $char = $string[$i];
  if ($char <= ord('Z') && $char >= ord('A') || $char >= ord('a') && $char <= ord('z') || $char == ord('_') || $char == ord('-')) {
    while ($char <= ord('Z') && $char >= ord('A') || $char >= ord('a') && $char <= ord('z') || $char == ord('_') || $char == ord('-')) {
      // identifier
      $buf .= $char;
      $char = $string[$i]; $i ++;
    }
    $tokens[] = array('IDENT', $buf);
  } else if (......) {
    // ......
  }
}
$tokens=array();
$string=“…代码…”;
$length=strlen($string);
$i=0;
而($i<$length){
$buf='';
$char=$string[$i];

如果($char=ord('A')| |$char>=ord('A')&&$char=ord('A')&&$char,这是一篇旧文章,但我仍然为此贡献了2美分。 在问题中,有一件事会严重减慢原始代码的速度,那就是下面这行代码:

$string = substr($string, strlen($matches[0]));

与其处理整个字符串,不如只处理其中的一部分(比如50个字符),这对于所有可能的正则表达式来说都足够了。然后,在其上应用相同的代码行。当该字符串收缩到预设长度以下时,向其加载更多数据。

Profile。使用XDebug生成分析数据并将其加载到KCacheGrind中。避免运行substr()如果可能的话,在源字符串上一遍又一遍地重新分配字符串并不是免费的。并且找到一些方法来减少您计算的正则表达式的数量。或者,更好的是,停止使用正则表达式。显而易见的D'oh!不是阅读lexer如何真正工作。关键思想是它们组成一组模式匹配(你称它们为regexp)到一个匹配器中,就像它一次应用所有匹配器一样“您的一次一个尝试模式的方案在性能上非常接近。问题是,我事先不知道令牌长度。例如,@-令牌可能是'@charset'、'@namespace'或'@import',但也可能是'@-moz document'之类的任意内容。它被定义为'@'后跟一个或多个[a-zA-Z0-9_-]或转义序列。”(如
\10FFFF
)或者任何非ASCII Unicode字符。我可以放弃
preg_match
,只处理“@”后面的字符,但是如果它是非ASCII字符,或者允许ASCII或ASCII作为转义序列的一部分,那么我必须逐字符进行测试,我认为这就是正则表达式引擎优化的目的。原则上,这是可行的(只要内存限制不被破坏)。但是,之后我必须循环遍历每个匹配,并找出它是什么类型的令牌。它可能会工作(在许多情况下,只需查看第一个字符串就足够了),但我怀疑它是否快得多。感谢指针。我将查看它(尤其是生成的代码).照erik的建议去做。除非你了解lexer生成器为你提供了什么以及它是如何工作的,否则你不会理解它为什么能如此惊人地快速地对输入流进行lex。只有链接的答案并没有那么好。但在这些答案中,链接断开的答案是完全无用的。
$string = substr($string, strlen($matches[0]));