用于匹配URL的改进的自由,准确的正则表达式模式

更新,2014年2月

我在Gist上发布了两个改进版本的原始URL匹配正则表达式模式。第一次尝试匹配任何URL,包括“mailto:[email protected]“,”x-whatever:// foo“等;第二次尝试仅匹配Web URL(http和https)我强烈建议使用这些模式而不是下面的原始模式给定某些输入和某些正则表达式引擎,旧模式可能导致锁定尝试匹配包含字面括号字符的URL新的那些不太聪明,但在我的测试中同样有效,并且(我相信)避免了在给出错误输入的情况下楔入正则表达式引擎的可能性它还包含一个大而丑陋但有用的TLD列表。

我保持原始文章的完整性,如下所示,但如果您只是在寻找匹配URL的正则表达式模式,请使用Gist上的那些(我将及时更新任何未来的改进。)

原创文章,自2010年7月起

回到十一月,我发布了用于匹配URL的正则表达式模式它似乎已被证明对其他人非常有用,而且,更好的是,基于那些使用它的人的反馈,我已经从几个方面改进了它。

The problem the pattern attempts to solve: identify the URLs in an arbitrary string of text, where by “arbitrary” let’s agree we mean something unstructured such as an email message or a tweet.

因此,这是一种尝试匹配任何类型的URL的模式,使用扩展的多行正则表达式格式,忽略文字空白并允许注释,这解释了模式的工作原理:

(?xi)
\b
(                           # Capture 1: entire matched URL
  (?:
    [a-z][\w-]+:                # URL protocol and colon
    (?:
      /{1,3}                        # 1-3 slashes
      |                             #   or
      [a-z0-9%]                     # Single letter or digit or '%'
                                    # (Trying not to match e.g"URI::Escape")
    )
    |                           #   or
    www\d{0,3}[.]               # "www.", "www1.", "www2." … "www999."
    |                           #   or
    [a-z0-9.\-]+[.][a-z]{2,4}/  # looks like domain name followed by a slash
  )
  (?:                           # One or more:
    [^\s()<>]+                      # Run of non-space, non-()<>
    |                               #   or
    \(([^\s()<>]+|(\([^\s()<>]+\)))*\)  # balanced parens, up to 2 levels
  )+
  (?:                           # End with:
    \(([^\s()<>]+|(\([^\s()<>]+\)))*\)  # balanced parens, up to 2 levels
    |                                   #   or
    [^\s`!()\[\]{};:'".,<>?«»“”‘’]        # not a space or one of these punct chars
  )
)

这是简洁的单行格式中的相同模式:

(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))

(你认为多行版本看起来很疯狂,对吗?)

这是我使用的测试数据同时锐化模式就像11月的模式一样,它首先尝试实用它不会尝试根据任何官方规范解析URL它不限于预定义的URL协议关于像括号和尾随标点符号这样的东西应该很聪明。

除了对它匹配的URL持开放态度之外,关于它使用哪个正则表达式引擎的模式也很自由我用Perl,PCRE(用于PHP,BBEdit和许多其他地方)和Oniguruma(用于Ruby,TextMate和许多其他地方)测试过它它也适用于所有现代JavaScript解释器如果你找到一个现代的正则表达式引擎,其模式不起作用,请告诉我。

与前一个模式相比,新模式的一些优点:

  • 它不再使用了[:PUNCT:]命名字符类我认为这在现代正则表达式引擎中得到普遍支持,但显然它不是。

  • 使用包含文字括号的URL做得更好,正确匹配前一个模式没有的以下URL:

    http://foo.com/more_(than)_one_(parens)
    http://foo.com/blah_(wikipedia)#cite-1
    http://foo.com/blah_(wikipedia)_blah#cite-1
    http://foo.com/unicode_(✪)_in_parens
    http://foo.com/(something)?after=parens
  • 它现在匹配邮寄地址:网址。

  • 它正确地猜测像“bit.ly/foo”和“is.gd/foo/”这样的东西是URL基本上:东西 - 点东西 - 斜线 - 东西。

括号匹配改进中包括最多匹配两个平衡的嵌套括号的能力 - 括号内的括号有一些奇特的方法可以使用动态或递归正则表达式模式来匹配任意深度的平衡括号,但这些动态/递归模式结构都是特定于单个正则表达式实现的也就是说,有一种方法可以用于PCRE,这是Perl的一种不同方式 - 在大多数正则表达式引擎中,根本没办法做到这一点。对模式进行硬编码以支持两级嵌套括号应该可以在任何地方使用,实际上,我只收到两个报告:实际具有第二级括号的现实URL,以及没有超过两个。

最后,我收到了一些关于该模式版本的请求只要匹配网址 - http,https和“www.example.com”之类的内容这是一个扩展格式模式,它执行此操作:

(?xi)
\b
(                       # Capture 1: entire matched URL
  (?:
    https?://               # http or https protocol
    |                       #   or
    www\d{0,3}[.]           # "www.", "www1.", "www2." … "www999."
    |                           #   or
    [a-z0-9.\-]+[.][a-z]{2,4}/  # looks like domain name followed by a slash
  )
  (?:                       # One or more:
    [^\s()<>]+                  # Run of non-space, non-()<>
    |                           #   or
    \(([^\s()<>]+|(\([^\s()<>]+\)))*\)  # balanced parens, up to 2 levels
  )+
  (?:                       # End with:
    \(([^\s()<>]+|(\([^\s()<>]+\)))*\)  # balanced parens, up to 2 levels
    |                               #   or
    [^\s`!()\[\]{};:'".,<>?«»“”‘’]        # not a space or one of these punct chars
  )
)

以下是单行格式的相同模式:

(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))

和以前一样,欢迎提出建议和改进,包括向我发送当前模式失败的示例输入。

更新:一些读者已经询问了在自己的代码中使用此模式的许可条款任何人都可以免费使用此模式,不附带任何附加条件考虑它是公共领域。