【问题标题】:Replace URL or fully qualified domain name with link用链接替换 ​​URL 或完全限定域名
【发布时间】:2012-05-30 09:19:51
【问题描述】:

我不擅长常规经验。 我想将链接更改为文本中的超链接

例如

Hello http://stackoverflow.com
Hello www.stackoverflow.com

我想让stackoverflow链接

Hello <a href='http://stackoverflow.com'>http://stackoverflow.com</a>
Hello <a href='http://www.stackoverflow.com'>www.stackoverflow.com</a>

我用过这个

var exp = /(\b(https?|ftp|file|):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
        return str.replace(exp,"<a href='$1' target='_blank'>$1</a>"); 

但这仅适用于 http://

提前致谢

【问题讨论】:

  • 没有协议很难弄清楚什么是链接。人们有时会这样写。这是链接吗? :)
  • 您将很难匹配“stackoverflow.com”,而不会冒险将文本变成人们不希望的链接。
  • OK 我应该怎么做才能使它像上面的任何提示?
  • 为了防止误报,不要自动链接任何没有协议的东西。做他们在这里做的事情并使用尖括号来强制链接,比如 .
  • @user457104 你能写下新的正则表达式吗?

标签: javascript regex


【解决方案1】:

正如其他人所说,首先您需要定义什么是“链接”。 (请注意,在这种情况下,“链接”只是“超链接”的缩写,所以你的这句话没有意义。)考虑到你的两个例子,你想匹配Uniform Resource Identifiers (URIs)Fully Qualified Domain Names (FQDNs)

为此,您应该使用可以在RFC 3986, Appendix B 中找到的正则表达式——

^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?

– 并将其更改为在字边界处匹配,仅接受 // 之后的 FQDN 和可选端口号,并在空格处停止 (\s):

      ,----scheme----.   ,-Fully Qualified Domain Name-.,-port.,--path--.,---query----.,fragment
      |              |   |                             ||     ||        ||            ||       |
(^|\s)(([^:/?#\s]+):)?(//([A-Za-z0-9-]+\.)+[A-Za-z0-9-]+(:\d+)?([^?#\s]*)(\?([^#\s]*))?(#(\S*))?

然后你可以让方案部分成为可选的——

                          |
                          v
(^|\s)((([^:/?#\s]+):)?//)?(([A-Za-z0-9-]+\.)+[A-Za-z0-9-]+(:\d+)?([^?#\s]*)(\?([^#\s]*))?(#(\S*))?

– 并将其写为正则表达式文字(表达式中的斜杠需要转义,因为它们用作分隔符):

/(^|\s)((([^:\/?#\s]+):)?\/\/)?(([A-Za-z0-9-]+\.)+[A-Za-z0-9-]+)(:\d+)?([^?#\s]*)(\?([^#\s]*))?(#(\S*))?/

(您可能也想匹配 IDNs;JSX:regexp.js 及其对 Unicode 字符属性的支持可以帮助您,请参阅 How to remove all characters from a string。您可能希望在 FQDN 子表达式前面添加一个子表达式用于代理访问的 URI (\w+@)? 中可选且不推荐使用的用户名传输。)

然后您可以替换 all 字符串(global 修饰符),这将与相应的 a 元素匹配:

var rx = /(^|\s)(((([^:\/?#\s]+):)?\/\/)?(([A-Za-z0-9-]+\.)+[A-Za-z0-9-]+)(:\d+)?([^?#\s]*)(\?([^#\s]*))?(#(\S*))?)/g;

str = str.replace(rx,
  function (match, optionalWhitespace, uri, scheme, p4, protocol, fqdn, p7, port,
            path, query, queryVal, fragment, fragId) {
    return (optionalWhitespace ? optionalWhitespace : '')
      + '<a href="' + (protocol ? uri : 'http://' + uri)
      + '" target="_blank">' + uri + '<\/a>';
  });

您必须在此假设,当您只看到 FQDN 前缀时,它是不安全网站的域名,并在前面加上 http://。否则,href 属性中的 URI 引用将引用 your 网站中可能存在的路径,该路径以域名作为其名称 (http://your-site.example/other-site.example.com),这可能不是您想要的.

在您的情况下,此表达式可能匹配太多,但不太可能;使用尽可能多的输入进行测试,并根据需要进行调整。如果向后兼容性不是问题,请使用non-capturing parentheses ((?:…)) 以提高效率并减少命名参数;详情请见ECMAScript Support Matrix

捕获 FQDN 部分——([A-Za-z0-9-]+\.)+[A-Za-z0-9-]+ 周围的括号——是可选的;您可能希望使用它为 a 元素提供 class 属性值,以便以特殊方式对其进行格式化,例如添加合适的图标,例如用于指向 Stack Overflow、Wikipedia、Twitter 或 Facebook 的链接。

您可能还想重新考虑使用 target 属性 (for Strict (X)HTML, you MUST remove it)。用户可能不会善意地认为无法控制链接目标的打开位置。改为以标题、图标、光标等形式提供提示。

【讨论】:

    【解决方案2】:

    使用此代码:

    var exp = /(((?:(?:https?|ftp|file):)?\/\/)?(?:[\w-]+\.)?[\w-]+\.\w{2,5}(?:\/[^\s\/]*)*)/ig;
    return str.replace(exp, function(_, link, protocol){
        return link.link(protocol ? link : "http://" + link);
    }); 
    

    【讨论】:

    • 啊,现在我明白了。您的问题是该协议可以被忽略。编辑了上面的代码。
    • 表情太贪心了;它将匹配任何文本。您在哪些“旧浏览器”中观察到 $&amp; 不可用?
    • 听说有些老的IE不支持。我正在处理表达式。
    • 好的,现在可以了。并且正则表达式变得极其丑陋。
    • 非常感谢,我很快就会开始学习正则表达式,所以我会继续努力,但现在它对我很有用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 2015-05-11
    • 1970-01-01
    • 2014-01-25
    • 2014-05-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多