如何编写JavaScript正则表达式来用HTML超链接替换格式为[*] (*)的超链接?

6

我需要解析以下格式中带有链接的文本:

[html title](http://www.htmlpage.com)
http://www.htmlpage.com
http://i.imgur.com/OgQ9Uaf.jpg

这两个字符串的输出结果将会是:
<a href='http://www.htmlpage.com'>html title</a>
<a href='http://www.htmlpage.com'>http://www.htmlpage.com</a>
<a href='http://i.imgur.com/OgQ9Uaf.jpg'>http://i.imgur.com/OgQ9Uaf.jpg</a>

字符串中可能包含任意数量的这些链接,即:
[html title](http://www.htmlpage.com)[html title](http://www.htmlpage.com)
[html title](http://www.htmlpage.com)   [html title](http://www.htmlpage.com)
[html title](http://www.htmlpage.com) wejwelfj http://www.htmlpage.com

输出:

<a href='http://www.htmlpage.com'>html title</a><a href='http://www.htmlpage.com'>html title</a>
<a href='http://www.htmlpage.com'>html title</a>    <a href='http://www.htmlpage.com'>html title</a>
<a href='http://www.htmlpage.com'>html title</a> wejwelfj <a href='http://www.htmlpage.com'>http://www.htmlpage.com</a>

我有一个非常冗长的函数,通过3次传递字符串可以完成不错的工作,但是我无法成功解析这个字符串:

[This](http://i.imgur.com/iIlhrEu.jpg) one got me crying first, then once the floodgates were opened [this](http://i.imgur.com/IwSNFVD.jpg) one did it again and [this](http://i.imgur.com/hxIwPKJ.jpg). Ugh, feels. Gotta go hug someone/something.

为了简洁起见,我将发布我尝试过的正则表达式,而不是整个查找/替换函数:

var matchArray2 = inString.match(/\[.*\]\(.*\)/g);

针对匹配[*](*)的问题,不起作用是因为它匹配了[]()[]()

就是这样,我想。一旦我进行了这个匹配,我会搜索该匹配项以查找()和[]以解析出链接和链接文本并构建href标记。我从临时字符串中删除匹配项,这样当我进行第二次查找以查找普通超链接时,就不会再次匹配它们:

var plainLinkArray = tempString2.match(/http\S*:\/\/\S*/g);

我不是用正则表达式解析任何HTML。我正在解析一个字符串,并尝试输出HTML。

编辑:我事后添加了要求它解析第三个链接http://i.imgur.com/OgQ9Uaf.jpg

我的最终解决方案(基于@Cerbrus的答案):

function parseAndHandleHyperlinks(inString)
{
    var result = inString.replace(/\[(.+?)\]\((https?:\/\/.+?)\)/g, '<a href="$2">$1</a>');
    return result.replace(/(?: |^)(https?\:\/\/[a-zA-Z0-9/.(]+)/g, ' <a href="$1">$1</a>');     
}

1
你尝试过什么?就像许多人在这里告诉你的那样,使用正则表达式解析 HTML... 这是一条疯狂之路,正如你可以在这里看到的那样。如果你要处理的标记只有一种,那么这是可能的,但请考虑其他选择。 - Elias Van Ootegem
我实在想不到哪里会用到 that ... - jahroy
@jahroy: 你看过这里的网址是如何制作的吗?让我给你一个提示:[标题](网址)[标题][1] <....> [1]:网址。像这样的解析器在论坛和其他社区网站上非常有用。 - Cerbrus
1
此外,@EliasVanOotegem:尝试解释HTML文档和尝试将一个特定格式解析为HTML之间存在区别。 - Cerbrus
最终解决方案对于这样的字符串无效: (https://example.com/the-new-control-plane/generating-self-signed-certificates-on-windows-7812a600c2d8) - user1892777
显示剩余4条评论
3个回答

10

尝试使用这个正则表达式:

/\[(.+?)\]\((https?:\/\/[a-zA-Z0-9/.(]+?)\)/g

var s = "[html title](http://www.htmlpage.com)[html title](http://www.htmlpage.com)\n\
[html title](http://www.htmlpage.com)   [html title](http://www.htmlpage.com)\n\
[html title](http://www.htmlpage.com) wejwelfj http://www.htmlpage.com";

s.replace(/\[(.+?)\]\((https?:\/\/[a-zA-Z0-9/.(]+?)\)/g, '<a href="$2">$1</a>');

正则表达式解释:

# /                   - Regex Start
# \[                  - a `[` character (escaped)
# (.+?)               - Followed by any amount of words, grouped, non-greedy, so it won't match past:
# \]                  - a `]` character (escaped)
# \(                  - Followed by a `(` character (escaped)
# (https?:\/\/
#   [a-zA-Z0-9/.(]+?) - Followed by a string that starts with `http://` or `https://`
# \)                  - Followed by a `)` character (escaped)
# /g                  - End of the regex, search globally.

现在,括号中的2个字符串() / []被捕获,并放置在以下字符串中:

'<a href="$2">$1</a>';

这适用于您的“有问题”的字符串:

var s = "[This](http://i.imgur.com/iIlhrEu.jpg) one got me crying first, then once the floodgates were opened [this](http://i.imgur.com/IwSNFVD.jpg) one did it again and [this](http://i.imgur.com/hxIwPKJ.jpg). Ugh, feels. Gotta go hug someone/something."
s.replace(/\[(.+?)\]\((https?:\/\/[a-zA-Z0-9/.(]+?)\)/g, '<a href="$2">$1</a>')

// Result:

'<a href="http://i.imgur.com/iIlhrEu.jpg">This</a> one got me crying first, then once the floodgates were opened <a href="http://i.imgur.com/IwSNFVD.jpg">this</a> one did it again and <a href="http://i.imgur.com/hxIwPKJ.jpg">this</a>. Ugh, feels. Gotta go hug someone/something.'

以下是一些“不正确”的输入示例:

var s = "[Th][][is](http://x.com)\n\
    [this](http://x(.com)\n\
    [this](http://x).com)"
s.replace(/\[(.+?)\]\((https?:\/\/[a-zA-Z0-9/.(]+?)\)/g, '<a href="$2">$1</a>')

//   "<a href="http://x.com">Th][][is</a>
//    <a href="http://x(.com">this</a>
//    <a href="http://x">this</a>.com)"

不能真正责怪最后一行的断开,因为无法知道用户是否打算在那里停止 url。

为了捕获松散的 url,请添加以下内容:

.replace(/(?: |^)(https?\:\/\/[a-zA-Z0-9/.(]+)/g, ' <a href="$1">$1</a>');
(?: |^)部分捕获了一个字符串开头空格字符,因此它也会匹配以url开头的行。

是的,要解析带括号的href。但在进行替换后,我很难解析普通的href(因为这些新的超链接现在都匹配)。@Explosion Pills有一个解决方案,但它使用了Javascript不支持的look-behind。 - BrennanR
[html title](http://www.htmlpage.com) wejwelfj http://www.htmlpage.com 转换为 <a href='http://www.htmlpage.com'>html title</a> wejwelfj <a href='http://www.htmlpage.com'>http://www.htmlpage.com</a> 没有被处理。否则问题已经解决。 - BrennanR
@BrennanR:下次您可能想在问题中提及这些额外的标准。已修复正则表达式。 - Cerbrus
哈哈,我猜我知道你可以这样做,但从风格上讲,我通常不这样做。当我的代码行开始换行时,我会感到紧张 :P - BrennanR
这种方法的缺点是,正则表达式会尝试抓取最近的 [,然后从中搜索最近的 ],该 ] 后面跟着一个在 () 中的 http 链接。因此,像 a[1] will look like [this](http://link.to/picture) 这样的文本将具有此部分文本的超链接 1 will look like this。Markdown 的 SO 实现仅正确地为单词 this 创建超链接。 - nhahtdh
显示剩余6条评论

6
str.replace(/\[(.*?)\]\((.*?)\)/gi, '<a href="$2">$1</a>');

这假设字符串中没有错误的括号,URL中没有括号。

然后:
str.replace(/(\s|^)(https?:\/\/.*?)(?=\s|$)/gi, '$1<a href="$2">$2</a>')

此正则表达式匹配类似于"http"的URL,该URL不是紧跟着一个引号(因为前一次替换添加了引号)。如果您有更好的表达方式,请随意使用。注意,JS没有回顾语法。相反,您可以看到表达式匹配任何空格或行的开头,以匹配普通的“http”链接。捕获的空格必须被放回(因此使用$1)。最后进行顺序环视以确保捕获所有直到下个空格(或表达式结尾)的内容。如果空格不是一个好的边界,您将不得不想出更好的边界。

你的第一个替换会把标题和URL放在错误的位置。 - Cerbrus
第一个正则表达式似乎有效。当我使用以下内容时,第二个正则表达式显示“无效的限定符”: var result2 = result.replace(/(?<!")(https?://.*?)\b/, '<a href="$1">$1</a>'); Firefox的错误控制台指向replace函数内部的初始/。 - BrennanR
2
似乎这不起作用是因为JavaScript不支持“向后查找”。 - BrennanR
这个解决方案太过宽泛。 - nhahtdh
@nhahtdh 你所说的“太松”是什么意思? - Explosion Pills
显示剩余2条评论

3
似乎您正在尝试将Markdown语法转换为HTML。Markdown语法尚未有规范(我指的是语法,而不是行为规范),因此您将盲目地走路,并尝试在途中加入您不想要的行为修复,同时还要重新发明轮子。我建议您使用现有的实现而不是自己编码。例如,Pagedown 是一个JS Markdown实现,目前在StackOverflow中使用。

如果您仍然想要一个正则表达式解决方案,请参考下面的尝试。请注意,我不知道它是否会与您进展中的其他Markdown功能良好兼容(如果您真的需要的话)。

/\[((?:[^\[\]\\]|\\.)+)\]\((https?:\/\/(?:[-A-Z0-9+&@#\/%=~_|\[\]](?= *\))|[-A-Z0-9+&@#\/%?=~_|\[\]!:,.;](?! *\))|\([-A-Z0-9+&@#\/%?=~_|\[\]!:,.;(]*\))+) *\)/i

上面的正则表达式应该捕获Pagedown中链接样式[description](url)的某些部分(我不确定它是否捕获了所有内容,因为Pagedown的源代码过于复杂,无法一次性阅读)。上面的正则表达式来自于Pagedown源代码中使用的2个不同的正则表达式混合而成。
一些功能包括:
  • 捕获组1包含[]内的文本,捕获组2包含URL。
  • 允许在文本部分[]中使用\转义[],例如[a\[1\]](http://link.com)。但是需要进行额外的处理。
  • 允许链接内有1级(),在类似这样的情况下非常有用:[String.valueOf](http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#valueOf(double))
  • 允许链接后有空格,然后再有)
我没有考虑这个正则表达式中的裸链接。
参考资料:

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