正则表达式以匹配通用URL

问题描述

| 我四处张望,还没有找到一个解决方案来满足我对与通用URL相匹配的正则表达式模式的需求。我需要支持多种协议(带有验证),本地主机和/或IP地址,端口和查询字符串。一些例子: http:// localhost / mysite https://本地主机:55000 ftp://192.1.1.1 telnet://somesite/page.htm?a = 1&b = 2 理想情况下,我希望该模式还支持提取各种元素(协议,主机,端口,查询字符串等),但这不是必需的。 (此外,出于我自己和将来读者的目的,如果您可以解释这种模式,那将很有帮助。)     

解决方法

        Nicholas Carey正确地指导您迈向RFC-3986。他指出的正则表达式将匹配通用URI,但不会对其进行验证(并且此正则表达式不利于从\“ wild \”中提取URL-它过于宽松,几乎与任何字符串匹配,包括空字符串) )。 关于验证要求,您可能需要看一下我写的关于该主题的文章,该文章摘自附录A中所有各个组件的所有ABNF语法定义,并提供了regex等效项: 正则表达式URI验证 关于从“野生”中选择URL的主题,请看Jeff Atwood的“ URL的问题”和John \'Gruber的“改进的自由,准确的”。匹配URL的正则表达式模式”博客文章,以了解可能出现的一些细微问题。另外,您可能想看看我去年开始的一个项目:URL链接化-这将从可能已经具有某些链接的文本中选择未链接的HTTP和FTP URL。 就是说,以下是一个PHP函数,它使用RFC-3986 \“绝对URI \”正则表达式的略微修改版本来验证HTTP和FTP URL \(使用此正则表达式,命名主机部分不能为空) 。 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}:){3}
                  | (?:(?:[0-9A-Fa-f]{1,2}[0-9A-Fa-f]{1,4}:){2}
                  | (?:(?:[0-9A-Fa-f]{1,3}[0-9A-Fa-f]{1,4})?::   [0-9A-Fa-f]{1,4}:
                  | (?:(?:[0-9A-Fa-f]{1,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,5}[0-9A-Fa-f]{1,4}
                |   (?:(?:[0-9A-Fa-f]{1,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查找系统(其中每个点分隔的子域为63个字符或更少,由数字,字母和数字组成)验证(命名的)主机部分(当它不是IP文字或IPv4地址时)。破折号,总长度小于255个字符。) 请注意,此功能的结构允许轻松扩展以包括其他方案。     ,RFC 3986 / STD 0066(统一资源标识符(URI):通用语法)的附录B提供了所需的正则表达式:   附录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
     朝相反的方向,我们可以从   通过使用第5.3节的算法来确定其组件。 至于针对特定方案验证URI,您需要查看描述您感兴趣的方案的RFC,以获取验证URI对于该方案有效所需的详细信息它声称是。 URI方案注册表位于http://www.iana.org/assignments/uri-schemes.html。 即使那样,您也注定要遭受某种失败。考虑“ 8”方案。您无法验证它是否代表“ 9”文件系统中的有效路径(除非您是授权者)。您能做的最好的事情就是验证它所代表的东西看起来像一条有效的路径。即使这样,Windows文件:像
file:///C:/foo/bar/baz/bat.txt
这样的url对于(将)对运行某种Windows风格的服务器来说都是无效的。任何运行* nix的服务器都可能会阻塞它(无论如何是驱动器号?)。     ,        这会在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\";
}