如果用户名不是每次都出现,您可以将该部分设为可选,并使用 2 个捕获组和preg_replace_callback。
在回调中检查组 1(用户名)的值是否不为空,并使用组值来组装链接的值。
(?<!\S)(?:(@[^\s@]+)(?!\S)[^h]*(?:h(?!ttp)[^h]*)*+)?\K((?:https?|ftp|file)://\S+)
解释
(?<!\S)
断言左空白边界
(?:
非捕获组
(@[^\s@]+)
捕获组 1匹配用户名
(?!\S)
断言右空白边界
[^h]*
匹配 0 次以上的任何字符,除了h
(?:
非捕获组
h(?!ttp)[^h]*
h
仅在不直接跟随时匹配ttp
)*+
使用所有格量词关闭组并重复 0 次以上
)?
关闭组并使其成为用户名的选项
\K
忘记匹配的内容
(
捕获组 2
(?:https?|ftp|file)
匹配协议
://\S+
匹配://
后跟 1+ 次非空白字符
)
关闭组 2
正则表达式演示| php演示
$string = <<<STR
Hello @username you need to check this http://github.com
and @username you need to https://stackoverflow.com/questions/ask
or https://stackoverflow.com
STR;
$pattern = "~(?<!\S)(?:(@[^\s@]+)(?!\S)[^h]*(?:h(?!ttp)[^h]*)*+)?\K((?:https?|ftp|file)://\S+)~";
$result = preg_replace_callback($pattern, function($m){
return sprintf('<a href="%s">%s</a>', $m[2],$m[1] !== "" ? $m[1] : $m[2]);
}, $string);
echo $result;
输出
Hello @username you need to check this <a href="http://github.com">@username</a>
and @username you need to <a href="https://stackoverflow.com/questions/ask">@username</a>
or <a href="https://stackoverflow.com">https://stackoverflow.com</a>
笔记
@
如果用户名和 url 之间不能出现,你可以使用[^h@]*
而不是[^h]*