php中带有负lookback的正则表达式

php中带有负lookback的正则表达式,php,regex,Php,Regex,我正在使用preg_replace_回调对大量目录产品描述进行SEO,但在使用regex时遇到了一些困难 我想替换所有这些单词(帽子、衬衫),除了“men's”+0-2个单词之后的单词,例如“men's pretty black hat”,“men's long shirt”不应替换 以下是调试代码,在实际应用程序中,我使用回调为每个单词选择合适的替换: $str = "men's black hat, and orange shirt!"; preg_match_all('/((\s|\.\s

我正在使用preg_replace_回调对大量目录产品描述进行SEO,但在使用regex时遇到了一些困难

我想替换所有这些单词(帽子、衬衫),除了“men's”+0-2个单词之后的单词,例如“men's pretty black hat”,“men's long shirt”不应替换

以下是调试代码,在实际应用程序中,我使用回调为每个单词选择合适的替换:

$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);

谢谢

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

一个技巧是反转字符串并使用负lookaheads。那么,您“理想”想要做的是:

preg_match_all('/(?<!\bmen\'s\s+(\w+\s+){0,2})(hat|shirt)\b/i', $str, &$_matches);
然后使用
array\u map
反转所有结果


顺便说一下,
\b
被称为单词边界。它们可能是您想要使用的,而不是所有的
(\s | \.\s | \.\s | \!s | \!\s | \?| \?\s)

回溯必须是固定长度的,因此这种解决问题的方法不起作用

我知道你想让
preg\u relace\u callback
做得太多了。如果您想要执行复杂度超过某个级别的操作,那么放弃单个函数调用的便利性是合理的。以下是解决此问题的另一种方法:

  • 使用
    preg\u split
    将文本与标记
    preg\u split\u OFFSET\u CAPTURE
    一起拆分为单词,以便知道每个单词在原始文本中的显示位置
  • 迭代单词数组。现在很容易对数组进行“反向查找”,查看帽子或衬衫前面是否有您感兴趣的任何其他术语
  • 无论何时发现帽子或衬衫的正匹配,请使用从
    preg_split
    的偏移量和正匹配的(已知)长度来启动原始文本输入
  • 例如:

    $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;
    
    $str=“男式黑帽子和橙色衬衫!”;
    $targets=数组('hat','shirt');
    $shield=‘男士’;
    $bias=0;
    对于($i=0;$i

    你能澄清你的问题吗?您谈到了替换,但正在使用
    preg\u match\u all
    。另外,您希望从您提到的调试代码中得到什么结果?模拟可变长度查找的另一种方法是使用
    \K
    。在遇到
    \K
    之前匹配的任何内容都将被丢弃/“遗忘”,这有效地允许您匹配任何内容,然后仅在
    \K
    之后开始捕获。搜索“重新设置比赛开始”大约在比赛进行到一半时。没问题。我自己更喜欢\@Jon的解决方案,也不知道@Wiseguy的
    \K
    构造。谢谢,关于preg_split()和进一步处理的观点非常好。它只是工作。谢谢,伙计!唯一的问题是,如果替换“FOO”的长度与原始的
    $word
    不同,那么
    $offset
    在第一次迭代后是不正确的=>substr\u replace会弄乱原始文本:)我用
    $offsetCorrection+=(strlen($replacement)-strlen($word))
    @c0rewell:Yup解决了这个问题,这绝对是一个错误,我真的应该抓住它。谢谢你的反馈,我相应地修正了答案。
    $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;