代码之家  ›  专栏  ›  技术社区  ›  Jan Hančič

匹配文本字符串

  •  0
  • Jan Hančič  · 技术社区  · 15 年前

    我有一个网页,用户可以在其中添加微笑。我想限制每条评论的微笑数量。“系统”工作正常,但regex部分有一些问题。 我在配置文件中定义了自己的smilies,如下所示:

    $config['Smilies'] = Array (
        // irrelevant stuff
        'smilies' => Array (
            ':)' => 'smile.gif',
            ':(' => 'sad.gif',
            // some more smilies
            's:10' => 'worship.gif',
            's:11' => 'zip.gif',
            's:12' => 'heart.gif',
            // some more smilies
            's:1' => 'dry.gif',
            's:2' => 'lol.gif',
            's:3' => 'lollol.gif',
            // some more smilies
        )
    );
    

    然后,当我验证注释(查看有多少个微笑)时,我通过这个数组循环,并将微笑与注释的内容相匹配。regex的用法如下:

    foreach ( $this->config['smilies'] as $smilie => $smilieImage )
    {
        $matches = Array ();
        Preg_Match_All ( '/' . Preg_Quote ( $smilie ) . '/i', $Content, $matches );
    
        $numOfFoundSmilies += Count ( $matches[0] );
    }
    

    问题是 如果我在注释中输入“s:10”,上面的代码将找到两个匹配项:“s:10”和“s:1”。我对正则表达式的了解很差,我想不出这一点。

    5 回复  |  直到 15 年前
        1
  •  3
  •   Boldewyn    15 年前

    正则表达式是 greedy 默认情况下(至少为pcres)。通常你可以绕过这个:

    /a+/ # selects the whiole string from "aaaaaaa"
    
    /a+?/ # selects only "a"
    

    在你的例子中,这没有多大帮助,因为你不能只在某个地方投一个问号。唯一的可能是 重新排序 你的搜索阵列和即时 代替 找到的地方。搜索 第一 对于 s:10 第二 对于 s:1 及使用 preg_replace() 而不是匹配。这样,第二个就不会再找到第一个了。

    另一种可能性: 将搜索数组分成两部分。如果您知道,那个总是有结构的:'加上数字,您可以在第二个循环中使用regexp,就像

    Preg_Match_All ( '/' . Preg_Quote ( $smilie ) . '(?![0-9])/i', $Content, $matches );
    

    具有 (?![0-9]) look ahead expression 寻找任何 -数字。

    还有第三个: 如果只允许(=convert)在某些地方使用笑脸,则可以使用此选项:

    Preg_Match_All ( '/\b' . Preg_Quote ( $smilie ) . '\b/i', $Content, $matches );
    

    \b 是一个“单词边界”,通常是任何非“(字母、数字、下划线)。缺点很明显,并不是所有的笑脸(如“abc;-)xyz”)都能找到。

        2
  •  4
  •   Victor Nicollet    15 年前

    对于每个微笑代码,您的代码都会计算该代码在日志中出现的次数,因此“s:10”会同时计算为“s:10”和“s:1”。

    一个解决方案是一次查找所有的微笑代码,这样每一篇文章都只对一个微笑代码计数。这可以通过将所有代码组合成一个regex来完成。

    $codes = array_keys($smilie);
    $escCodes = array_map('preg_quote', $codes);
    $regex = '/'.implode('|',$escCodes).'/i';
    
    preg_match_all($regex, $Content, $matches);
    
    $found = count($matches);
    
        3
  •  1
  •   Gordon Haim Evgi    15 年前

    我可以想象这个代码比regex更快

    $replaced = str_replace(array_keys($config['Smilies']), 
                            array_values($config['Smilies']),
                            $message, $count);
    

    这不能解决问题 s:1 s:10 不过,我建议对此使用更清晰的分隔符/边界符号,例如 :s10: 而不是 S: 10 . 那就不再是问题了。

    另外,我建议无论如何不要使用数字标识符。用户可能会觉得记住它们很乏味。为什么不使用易于记忆的标签,例如 :heart: :lol: ?

        4
  •  0
  •   Residuum    15 年前

    你可以改变你的正则表达式来使用 word boundaries 或\s(空白)匹配,所以 s:1 变成 \bs:1\b \ss:1\s . 注意第二种方法 s:1. 将不匹配,两个版本将不匹配 This is my funny texts:1 .

        5
  •  0
  •   Dave Child    15 年前

    将“s:1”更改为“s:1[^0-9]”—与任何“s:1”匹配,后面不跟其他数字。