1

我正在尝试在用户发布到我的在线论坛的文本中查找电子邮件地址,并将其更改为公告板代码 (BBCode)。例如:

example@yahoo.com 

会成为:

[mail]exmaple@yahoo.com[/mail]  

但是我不想更改已经在 BBCode 中的电子邮件地址。例如,我不想要:

[mail]exmaple@yahoo.com[/mail]

成为:

[mail][mail]exmaple@yahoo.com[/mail][/mail]

因此,我需要在我的正则表达式中添加一个否定的后向断言,以确保电子邮件地址前面没有字符 [mail](或只是 ])。

我正在使用的 PHP 代码是:

$pattern = '#(?<!])([a-zA-Z0-9_\-\.]*@\S+\.\w+)#';
$bbcode = '[mail]$1[/mail]';
preg_replace($pattern, $bbcode, $text);

我遇到的问题是负面的后视仅适用于电子邮件地址寻求子模式的第一个字符。例如,当应用于文本时:

[mail]example@yahoo.com[/mail]

结果是:

[mail]e[mail]xample@yahoo.com[/mail][/mail]

因此,负向查找会找到 [mail]e,但电子邮件地址 xample@yahoo.com 的其余部分仍然有效。我意识到这是因为电子邮件地址寻求子模式的编写方式,因为它允许在 @ 之前有任意数量的字符。

如何更改正则表达式以使否定后向适用于寻求子模式的整个电子邮件地址,同时仍成功捕获大多数发布的电子邮件地址?

4

2 回答 2

0

您可以匹配和跳过之间的子字符串[mail][/mail]并且仅匹配您在所有其他上下文中拥有的电子邮件模式:

'~\[mail].*?\[/mail](*SKIP)(*F)|[a-zA-Z0-9_.-]*@\S+\.\w+~si'

要跳过任何标签中的匹配电子邮件,例如您拥有的标签:

'~\[(\w+)(?:\s+[^]]*)?].*?\[/\1](*SKIP)(*F)|[a-zA-Z0-9_.-]*@\S+\.\w+~si'

查看 PHP 演示:

$rx = '~\[mail].*?\[/mail](*SKIP)(*F)|[a-zA-Z0-9_.-]*@\S+\.\w+~si';
// Or, for any tag:
// $rx = '~\[(\w+)(?:\s+[^]]*)?].*?\[/\1](*SKIP)(*F)|[a-zA-Z0-9_.-]*@\S+\.\w+~si';
$text = preg_replace($rx, '[url=mailto:$0]$0[/url]', $text);

请参阅正则表达式演示 #1正则表达式演示 #2

细节

  • \[mail].*?\[/mail](*SKIP)(*F)- [mail],然后尽可能少的任何 0+ 字符,然后[/mail](*SKIP)(*F)丢弃匹配并从失败匹配的末尾开始下一次搜索
  • |- 或者
  • [a-zA-Z0-9_.-]*@\S+\.\w+- 类似电子邮件的模式。

$0在替换模式中指的是整个匹配值,不需要用捕获括号包裹整个模式。

注意:如果您的电子邮件提取正则表达式无法按预期工作,请检查在 PHP 中,如何从文本块中提取多个电子邮件地址并将它们放入数组中?.

于 2019-03-05T09:59:31.620 回答
0

您需要在正则表达式的开头有一个单词边界以避免部分匹配文本,并且还使用+代替*电子邮件正则表达式中的用户名部分。尝试使用这个正则表达式,

(?<!])\b([a-zA-Z0-9_\-\.]+@\S+\.\w+)(?!\[)

演示

于 2019-03-05T10:04:39.683 回答