在一个字符串中匹配单词,但排除一个特定的单词。使用PHP正则表达式。

5
我有一个文本($txt),一个单词数组($words),我想要添加一个链接和一个不被替换的词($wordToExclude)。
$words = array ('adipiscing','molestie','fringilla');
$wordToExclude = 'consectetur adipiscing';


$txt = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque
mattis tincidunt dolor sed consequat. Sed rutrum, mauris convallis bibendum 
dignissim, ligula sem molestie massa, vitae condimentum neque sem non tellus.
Aenean dolor enim, cursus vel sodales ac, condimentum ac erat. Quisque
lobortis libero nec arcu fringilla imperdiet. Pellentesque commodo, 
arcu et dictum tincidunt, ipsum elit molestie ipsum, ut ultricies nisl
neque in velit. Curabitur luctus dui id urna consequat vitae mattis
turpis pretium. Donec nec adipiscing velit.'

我想获得这个结果:

$txt = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque
mattis tincidunt dolor sed consequat. Sed rutrum, mauris convallis bibendum 
dignissim, ligula sem <a href="#">molestie</a> massa, vitae condimentum neque sem non tellus.
Aenean dolor enim, cursus vel sodales ac, condimentum ac erat. Quisque
lobortis libero nec arcu <a href="#">fringilla</a> imperdiet. Pellentesque commodo, 
arcu et dictum tincidunt, ipsum elit <a href="#">molestie</a> ipsum, ut ultricies nisl
neque in velit. Curabitur luctus dui id urna consequat vitae mattis
turpis pretium. Donec nec <a href="#">adipiscing</a> velit.'

你需要给我们一个简短的例子;阅读所有那些假拉丁文让我的眼睛疼痛。 - Bojangles
@TimPietzcker他在哪里说的?我理解的方式是:“我想用链接替换这些单词,只要这些单词不在$wordsToExclude中”,这看起来没有意义,因为$wordsToExclude将是多余的。 - JamesHalsall
现在我看到了,是我的错。 - JamesHalsall
@TimPietzcker 这个例子本可以更简单些。 - JamesHalsall
更复杂的是:$wordsToExclude 是否还可以包含像 adipiscing elitarcu fringilla imperdiet 这样的内容?部分单词?等等。你说得对,规格说明目前还不够清晰。 - Tim Pietzcker
OT:“Molestie massa”...我希望这个网站能够安全! - poisson
3个回答

3
$result = preg_replace(
    '/\b                 # Word boundary
    (                    # Match one of the following:
     (?<!consectetur\s)  #  (unless preceded by "consectetur "
     adipiscing          #  adipiscing
    |                    # or
     molestie            #  molestie
    |                    # etc.
     fringilla
    )                    # End of alternation
    \b                   # Word boundary
    /ix', 
    '<a href="#">\1</a>', $subject);

我认为他正在尝试构建一个具有3个参数的函数,即文本、单词数组和要排除的单词。他希望该函数替换单词数组中的所有单词,但在文本中排除其中一种组合。因此,它应该使用他的变量来实现通用性。 - NorthGuard
1
@inTide:是的。规格模糊不清,水晶球也碎了。 - Tim Pietzcker

2

好的!虽然我认为这在技术上是可行的,但我提供的解决方案目前有点含糊:

s%(?!consectetur adipiscing)(adipiscing|molestie|fringilla)(?<!consectetur adipiscing)%<a href="#LinkBasedUpon$1">$1</a>%s

将...

sit amet,consectetur adipiscing elit。Quisque... ligula sem <a href="#LinkBasedUponmolestie">molestie</a> massa... nec arcu <a href="#LinkBasedUponfringilla">fringilla</a> imperdiet... nec <a href="#LinkBasedUponadipiscing">adipiscing</a> velit。

变成...

sit amet,consectetur adipiscing elit。Quisque... ligula sem molestie massa... nec arcu fringilla imperdiet... nec adipiscing velit。

之所以它是软性解决方案,是因为它不能处理部分单词或其他情况,其中要排除的单词不以要匹配的单词之一开头或结尾。例如,如果我们在要排除的“word”(即consectetur adipiscing elit)后附加,那么该表达式最终会匹配consectetur adipiscing elit中的adipiscing,因为adipiscingconsectetur adipiscing elit不以相同的方式开头或结尾。

只要您要排除的“word”(A B C)始终以要查找的单词之一结尾或开头(C | X | E中有一个C,而A B C以单词C结尾),它就应该可以工作...

编辑 {

之所以'未匹配'的单词必须以匹配的单词之一开头或结尾,是因为此解决方案在匹配前使用否定先行断言,并在匹配后使用否定后行断言,以确保匹配的序列不匹配不要匹配的单词(这有意义吗?)

}

有某些解决方案,但它们要么需要更多处理器和编程工作,要么需要根据搜索的文本大小和列表中单词的数量以及特定要求呈指数级增长 - 而您没有指定任何其他内容,因此我不会在这一点上进行详细说明。如果这对您的情况足够好,请告诉我!


0

我看到你在用PHP做这件事。我理解你有一个单词数组需要在文本中查找并替换为链接,同时你还有一个需要排除的字符串。也许不用编写复杂的正则表达式,而是采用实用但可能不是最好的解决方案:

将任务分解为子任务:

  1. 使用preg_match_all查找所有被排除字符串的偏移量(你知道字符串长度(strlen),使用PREG_OFFSET_CAPTURE标志的preg_match_all,你将找到确切的开始和结束位置 - 如果有多个)
  2. 对单词列表进行foreach循环,并再次使用preg_match_all获取需要替换为链接的所有单词的出现次数
  3. 将步骤2中找到的位置与步骤1中找到的位置进行比较,如果它们在外部,则进行替换,如果有重叠则跳过

这肯定不会是一行代码,但编码起来相当容易,以后阅读起来也很容易。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接