在php中的正则表达式与负面的lookbehind

时间:2013-08-26 15:50:05

标签: php regex

我正在使用preg_replace_callback进行大量目录产品描述的SEO,并且在使用正则表达式时遇到一些困难。

我想替换所有这些单词(帽子,衬衫),除了“男人”+ 0-2之间的单词之外,例如“男人的漂亮黑帽子”,“男式长衬衫”不应该被替换。

这是一个调试代码,在实际应用中我使用回调来为每个单词选择正确的替换:

$str = "men's black hat, and orange shirt!";
preg_match_all('/((\s|\.\s|,\s|\!\s|\?\s)(hat|shirt)(\s|\.|\.\s|,\s|\!|\!\s|\?|\?\s))/i', $str, &$_matches);
print_r($_matches);

由于

2 个答案:

答案 0 :(得分:0)

我不认为可变长度的负面外观是可能的。

一个技巧是扭转字符串并使用负向前瞻。那么,你理想地“想要”做什么:

preg_match_all('/(?<!\bmen\'s\s+(\w+\s+){0,2})(hat|shirt)\b/i', $str, &$_matches);

你可以做到

preg_match_all('/\b(tah|trihs)(?!(\s+\w+){0,2}\s+s\'nem\b)/i', strrev($str), $rev_matches);

然后使用array_map反转所有结果。

顺便说一句,\b被称为字边界。它们可能是您的意思,而不是所有(\s|\.|\.\s|,\s|\!|\!\s|\?|\?\s)

答案 1 :(得分:0)

Lookbehind必须是固定长度的,所以这种攻击问题的方式不起作用。

恕我直言,你试图让preg_relace_callback做得太多。如果您想执行超出一定级别的复杂操作,则放弃单个函数调用的便利性是合理的。这是解决问题的另一种方法:

  1. 使用preg_split将文字与标记PREG_SPLIT_OFFSET_CAPTURE一起分割为单词,以便您知道每个单词在原始文本中的显示位置。
  2. 迭代一堆单词。现在很容易在阵列上做一个“负面观察”,看看帽子或衬衫前面是否有你感兴趣的任何一个术语。
  3. 每当您找到帽子或衬衫的正面匹配时,请使用preg_split的偏移量和正匹配的(已知)长度来为原始文本输入提供substr_replace
  4. 例如:

    $str = "men's black hat, and orange shirt!";
    $targets = array('hat', 'shirt');
    $shield = 'men\'s';
    $bias = 0;
    
    for ($i = 0; $i < count($words); ++$i) {
        list ($word, $offset) = $words[$i];
    
        if (!in_array($word, $targets)) {
            continue;
        }
    
        for ($j = max($i - 2, 0); $j < $i; ++$j) {
            if ($words[$j][0] === $shield) {
                continue 2;
            }
        }
    
        $replacement = 'FOO';
        $str = substr_replace($str, $replacement, $offset + $bias, strlen($word));
        $bias += strlen($replacement) - strlen($word);
    }
    
    echo $str;
    

    <强> See it in action