提问人:Czechmate 提问时间:8/8/2013 最后编辑:Andy LesterCzechmate 更新时间:8/9/2013 访问量:859
查找并替换文本 blob 中的 URL,但排除链接标记中的 URL
Find and replace URLs in a blob of text but exclude those in link tags
问:
我一直在尝试运行一个字符串并查找并用链接替换 URL,这是我到目前为止所提出的,它似乎在大多数情况下确实工作得很好,但是我想打磨一些东西。此外,它可能不是执行此操作的最佳方式。
我在 SO 上读过很多关于这个问题的帖子,虽然它有很大帮助,但我仍然需要把松散的结局绑起来。
我在字符串中跑了两次。我第一次用 html 标签替换 bbtags;第二次我运行字符串并用链接替换文本 url:
$body_str = preg_replace('/\[url=(.+?)\](.+?)\[\/url\]/i', '<a href="\1" rel="nofollow">\2</a>', $body_str);
$body_str = preg_replace_callback(
'!(?:^|[^"\'])(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?!',
function ($matches) {
return strpos(trim($matches[0]), 'thisone.com') == FALSE ?
'<a href="' . ltrim($matches[0], " \t\n\r\0\x0B.,@?^=%&:/~\+#'") . '" rel="nofollow">' . ltrim($matches[0], "\t\n\r\0\x0B.,@?^=%&:/~\+#'") . '</a>' :
'<a href="' . ltrim($matches[0], " \t\n\r\0\x0B.,@?^=%&:/~\+#'") . '">' . ltrim($matches[0], "\t\n\r\0\x0B.,@?^=%&:/~\+#'") . '</a>';
},
$body_str
);
到目前为止,我发现的几个问题是它倾向于在“http”等之前立即拾取字符,例如空格/逗号/冒号等,这会破坏链接。因此,我使用preg_replace_callback来解决这个问题,并修剪一些会破坏链接的不需要的字符。
另一个问题是,为了避免通过匹配已经在 A 标签中的 url 来破坏链接,我目前正在排除以引号、双引号开头的 url,我宁愿使用 href='|href=“ 进行排除。
任何提示和建议将不胜感激
答:
0赞
Thibault
8/9/2013
#1
首先,我允许自己重构一下您的代码,使其更易于阅读和修改:
function urltrim($str) { return ltrim($str, " \t\n\r\0\x0B.,@?^=%&:/~\+#'"); } function addlink($str,$nofollow=true) { return '<a href="' . urltrim($str) . '"'.($nofollow ? ' rel="nofollow"' : '').'>' . urltrim($str) . '</a>'; } function checksite($str) { return strpos(trim($str), 'thisone.com') == FALSE ? addlink($str) : addlink($str,false); } $body_str = preg_replace('/\[url=(.+?)\](.+?)\[\/url\]/i', '\2', $body_str); $body_str = preg_replace_callback( '!(?:^|[^"\'])(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?!', function ($matches) { return checksite($matches[0]); }, $body_str );
在那之后,我改变了你处理链接的方式:
- 我认为 URL 是一个单词(= 所有字符,直到您找到空格或 \n 或 \t (=\s))
- 我更改了匹配方法以匹配字符串前面存在 href=
- 如果它存在,那么我什么都不做,它已经是一个链接了
- 如果没有 href= 存在,那么我替换链接
- 所以 urltrim 方法不再有用了,因为我不会吃掉 http 之前的第一个字符
- 当然,我使用 urlencode 对 url 进行编码并避免 html 注入
function urltrim($str) { return $str; } function addlink($str,$nofollow=true) { $url = preg_replace("#(https?)%3A%2F%2F#","$1://",urlencode(urltrim($str))); return '<a href="' . $url . '"'.($nofollow ? ' rel="nofollow"' : '').'>' . urltrim($str) . '</a>'; } function checksite($str) { return strpos(trim($str), 'thisone.com') == FALSE ? addlink($str) : addlink($str,false); } $body_str = preg_replace('/\[url=(.+?)\](.+?)\[\/url\]/i', '\2', $body_str); $body_str = preg_replace_callback( '!(|href=)(["\']?)(https?://[^\s]+)!', function ($matches) { if ($matches[1]) { # If href= is present, dont do anything, return the original string return $matches[0]; } else { # add the previous char (" or ') and the link return $matches[2].checksite($matches[3]); } }, $body_str );
我希望这对你的项目有所帮助。 告诉我们是否有帮助。
再见。
评论