正则表达式以匹配通用URL

时间:2011-05-31 18:15:50

标签: javascript .net regex

我看了一遍,还没有找到一个解决方案,以满足我对正常表达式模式的需求,该模式将匹配通用URL。我需要支持多种协议(带验证),localhost和/或IP寻址,端口和查询字符串。一些例子:

理想情况下,我希望该模式也支持提取各种元素(协议,主机,端口,查询字符串等),但这不是必需的。

(另外,出于我和未来读者的目的,如果你能解释这种模式,那将会有所帮助。)

3 个答案:

答案 0 :(得分:5)

RFC 3986/STD 0066的附录B(统一资源标识符(URI):通用语法)提供了您需要的正则表达式:

  

附录B.使用正则表达式解析URI引用

     

由于“第一场比赛胜利”算法与“贪婪”相同   POSIX正则表达式使用的消歧方法,它是   自然而普通的使用正则表达式来解析   URI引用的潜在五个组件。

     

以下行是用于分解a的正则表达式   格式良好的URI引用到其组件中。

  ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
   12            3  4          5       6  7        8 9
     

上面第二行中的数字只是为了提高可读性;   它们表示每个子表达的参考点(即每个子表达式)   配对括号)。我们引用子表达式匹配的值   <n>$<n>。例如,将上述表达式与

匹配
  http://www.ics.uci.edu/pub/ietf/uri/#Related
     

导致以下子表达式匹配:

  $1 = http:
  $2 = http
  $3 = //www.ics.uci.edu
  $4 = www.ics.uci.edu
  $5 = /pub/ietf/uri/
  $6 = <undefined>
  $7 = <undefined>
  $8 = #Related
  $9 = Related
     

其中<undefined>表示组件不存在,原样   上例中查询组件的情况。因此,我们   可以确定五个组件的值

  scheme    = $2
  authority = $4
  path      = $5
  query     = $7
  fragment  = $9
     

反方向,我们可以从中重新创建URI引用   其组件使用5.3节的算法。

至于验证针对特定方案的URI,您需要查看描述您有兴趣获取所需详细信息的方案的RFC。验证URI对于它声称的方案是否有效。 URI方案注册表位于http://www.iana.org/assignments/uri-schemes.html

即便如此,你注定会遇到某种失败。考虑file:方案。您无法验证它是否代表authority文件系统中的有效路径(除非您权限)。您可以做的最好的事情是验证它表示看起来像有效路径的东西。即便如此,一个Windows文件:像file:///C:/foo/bar/baz/bat.txt这样的网址(除了运行某种Windows的服务器)之外的任何东西都是无效的。运行* nix的任何服务器都可能会阻塞它(无论如何,驱动器号是什么?)。

答案 1 :(得分:2)

Nicholas Carey引导您走向RFC-3986是正确的。他指出的正则表达式将匹配一个通用URI,但它不会验证它(并且这个正则表达式不适合从“野外”中选择网址 - 它太松散并且几乎与任何匹配包含空字符串的字符串)。

关于验证要求,您可能需要查看我在该主题上撰写的文章,该文章从附录A中获取了所有各种组件的所有ABNF语法定义,并提供了正则表达式等价物:

Regular Expression URI Validation

关于从“狂野”中挑选URL的主题,请查看Jeff Atwood的“The Problem With URLs”和John'Gruber的“An Improved Liberal, Accurate Regex Pattern for Matching URLs”博客文章,了解一些可能出现的微妙问题。另外,您可能想看看我去年开始的项目:URL Linkification - 这会从已经有一些链接的文本中挑选出未链接的HTTP和FTP URL。

也就是说,以下是一个PHP函数,它使用RFC-3986“绝对URI”正则表达式的略微修改版本来验证HTTP和FTP URL(使用此正则表达式,命名为 host 部分一定不能空着。 URI的所有各种组件都被隔离并捕获到命名组中,这样可以轻松地操作和验证程序代码中的各个部分:

function url_valid($url)
{
    if (strpos($url, 'www.') === 0) $url = 'http://'. $url;
    if (strpos($url, 'ftp.') === 0) $url = 'ftp://'. $url;
    if (!preg_match('/# Valid absolute URI having a non-empty, valid DNS host.
        ^
        (?P<scheme>[A-Za-z][A-Za-z0-9+\-.]*):\/\/
        (?P<authority>
          (?:(?P<userinfo>(?:[A-Za-z0-9\-._~!$&\'()*+,;=:]|%[0-9A-Fa-f]{2})*)@)?
          (?P<host>
            (?P<IP_literal>
              \[
              (?:
                (?P<IPV6address>
                  (?:                                                (?:[0-9A-Fa-f]{1,4}:){6}
                  |                                                ::(?:[0-9A-Fa-f]{1,4}:){5}
                  | (?:                          [0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}
                  | (?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}
                  | (?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}
                  | (?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::   [0-9A-Fa-f]{1,4}:
                  | (?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::
                  )
                  (?P<ls32>[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}
                  | (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
                       (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
                  )
                |   (?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::   [0-9A-Fa-f]{1,4}
                |   (?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::
                )
              | (?P<IPvFuture>[Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&\'()*+,;=:]+)
              )
              \]
            )
          | (?P<IPv4address>(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
                               (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))
          | (?P<regname>(?:[A-Za-z0-9\-._~!$&\'()*+,;=]|%[0-9A-Fa-f]{2})+)
          )
          (?::(?P<port>[0-9]*))?
        )
        (?P<path_abempty>(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)
        (?:\?(?P<query>       (?:[A-Za-z0-9\-._~!$&\'()*+,;=:@\\/?]|%[0-9A-Fa-f]{2})*))?
        (?:\#(?P<fragment>    (?:[A-Za-z0-9\-._~!$&\'()*+,;=:@\\/?]|%[0-9A-Fa-f]{2})*))?
        $
        /mx', $url, $m)) return FALSE;
    switch ($m['scheme'])
    {
    case 'https':
    case 'http':
        if ($m['userinfo']) return FALSE; // HTTP scheme does not allow userinfo.
        break;
    case 'ftps':
    case 'ftp':
        break;
    default:
        return FALSE;   // Unrecognised URI scheme. Default to FALSE.
    }
    // Validate host name conforms to DNS "dot-separated-parts".
    if ($m{'regname'}) // If host regname specified, check for DNS conformance.
    {
        if (!preg_match('/# HTTP DNS host name.
            ^                      # Anchor to beginning of string.
            (?!.{256})             # Overall host length is less than 256 chars.
            (?:                    # Group dot separated host part alternatives.
              [0-9A-Za-z]\.        # Either a single alphanum followed by dot
            |                      # or... part has more than one char (63 chars max).
              [0-9A-Za-z]          # Part first char is alphanum (no dash).
              [\-0-9A-Za-z]{0,61}  # Internal chars are alphanum plus dash.
              [0-9A-Za-z]          # Part last char is alphanum (no dash).
              \.                   # Each part followed by literal dot.
            )*                     # One or more parts before top level domain.
            (?:                    # Explicitly specify top level domains.
              com|edu|gov|int|mil|net|org|biz|
              info|name|pro|aero|coop|museum|
              asia|cat|jobs|mobi|tel|travel|
              [A-Za-z]{2})         # Country codes are exqactly two alpha chars.
            $                      # Anchor to end of string.
            /ix', $m['host'])) return FALSE;
    }
    $m['url'] = $url;
    for ($i = 0; isset($m[$i]); ++$i) unset($m[$i]);
    return $m; // return TRUE == array of useful named $matches plus the valid $url.
}

第一个正则表达式将字符串验证为绝对(具有非空主机部分)通用URI。第二个正则表达式用于验证关于DNS查找系统的(命名的)主机部分(当它不是IP文字或IPv4地址时)(其中每个以点分隔的子域为63个字符或由数字,字母和短划线组成,总长度小于255个字符。)

请注意,此功能的结构允许轻松扩展以包含其他方案。

答案 2 :(得分:0)

这有可能是Perl吗?

尝试:

use strict;
my $url = "http://localhost/test";
if ($url =~ m/^(.+):\/\/(.+)\/(.+)/) {
    my $protocol = $1;
    my $domain = $2;
    my $dir = $3;

    print "$protocol $domain $dir \n";
}