在不在超链接内寻找URL的正则表达式

8
有很多正则表达式可以匹配URL。然而,我想匹配的是不出现在<a>超链接标签(HREF,内部值等)中的URL。所以这些中没有一个URL应该匹配:
<a href="http://www.example.com/">something</a>
<a href="http://www.example.com/">http://www.example2.com</a>
<a href="http://www.example.com/"><b>something</b>http://www.example.com/<span>test</span></a>
<a></a>之外的任何URL都应该被匹配。
我尝试过的一种方法是使用负向先行断言来查看URL后面的第一个<a>标签是开放的<a>还是关闭的</a>。如果它是一个关闭的</a>,那么URL必须在超链接内。我认为这个想法还好,但是负向先行断言正则表达式没有工作(或者更准确地说,正则表达式没有正确编写)。非常感谢任何提示。

什么平台?Perl、.NET还是Java? - Peter Mortensen
6个回答

5

我也在寻找这个答案,但因为没有一个真正满足我的需求,所以我创建了以下的正则表达式。显然,由于这是一个正则表达式,所以请注意这不是一个完美的解决方案。

/(?!<a[^>]*>[^<])(((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?))(?![^<]*<\/a>)/gi

更新 HTML 的整个函数如下:

function linkifyWithRegex(input) {
  let html = input;
  let regx = /(?!<a[^>]*>[^<])(((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?))(?![^<]*<\/a>)/gi;
  html = html.replace(
    regx,
    function (match) {
      return '<a href="' + match + '">' + match + "</a>";
    }
  );
  return html;
}


https://regexr.com/7cab0 对我来说,它似乎不起作用。或者是我漏掉了什么吗? - Oleg Yablokov

2

您可以分两步来完成,而不是试图想出一个单一的正则表达式:

  1. 将HTML锚点部分(整个锚点标签:开标签、内容和闭标签)淡化(替换为空)。

  2. 匹配URL

在Perl中,可以这样写:

my $curLine = $_; #Do not change $_ if it is needed for something else.
$curLine =~ /<a[^<]+<\/a>//g; #Remove all of HTML anchor tag, "<a", "</a>" and everything in between.
if ( $curLine =~ /http:\/\//)
{
  print "Matched an URL outside a HTML anchor !: $_\n";
}

如果我删除(淡化)HTML锚点,我将无法确定URL最初是否位于超链接内部,对吗? 我只寻找在超链接标签之外的URL。 - Ben Amada
我的意思是:从开头的锚点标签到结束的锚点标签之间全部删除 - Peter Mortensen
啊,太好了,我解决了。一开始我以为你只是要删除开头和结尾的标签,但是删除整个标签才是关键。谢谢! - Ben Amada
-1 你应该通过一个合适的解析器来移除<a>元素,因为HTML不是一种常规语言。 - Svante
1
@Svante:我认为这不公平。难道不应该针对问题本身吗?问题是关于使用正则表达式进行匹配的。 - Peter Mortensen

0
 ^.*<(a|A){1,1}  ->scan until >a or >A is found
 .*(href|HREF){1,1}\=  -> scan until href= or HREF=
  \x22{1,1}.*\x22  -> accept all characters between two quotes
  > -> look for >
  .+(|){1,1} -> accept description and end anchor tag
  $ -> End of string search


    pattern= "^.*<(a|A){1,1}.*(href|HREF){1,1}.*\=.*\x22{0,1}.*\x22{0,1}.*>.+(|){1,1}$"

0

您可以使用一个正则表达式来匹配锚点和超链接:

# Note that this is a dummy, you'll need a more sophisticated URL regex
regex = '(<a[^>]+>)|(http://.*)'

然后循环遍历结果,仅处理第二个子模式被找到的匹配项。


这仅适用于位于标签内部的URL,而不适用于位于<a>元素内部的URL。此外,它尝试使用正则表达式解析非正则语言。 - Svante
@Svante:首先,您可以轻松地扩展示例以匹配<a...>和</a>之间的所有内容。然后它执行与已接受答案相同的操作,只需一次通过即可完成。其次,“它”不会尝试解析除基于HTML字符串出现之外的任何内容的常规语言。如果您只想在字符串中查找简单模式,则无需使用全功能HTML解析器。 - Ferdinand Beyer

0

Peter有一个很好的答案:首先,删除锚点,以便

Some text <a href="http://page.net">TeXt</a> and some more text with link http://a.net

被替换为

Some text  and some more text with link http://a.net

然后运行一个正则表达式来查找URL:

http://a.net

0
使用DOM过滤掉锚点元素,然后对剩余部分进行简单的URL正则表达式匹配。

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