4

我正在尝试提取 url 权限(没有协议和 www。如果存在)以及它之后的所有内容(如果存在)。到目前为止我的正则表达式:

/^(?:http|https)?(?::\/\/)?(?:www\.)?(.*?)(\/.*)/;

这适用于包含所有内容的网址,例如:

http://www.site.com/part1/part2?key=value#blub

但是,如果我将路径捕获组标记为可选:

/^(?:http|https)?(?::\/\/)?(?:www\.)?(.*?)(\/.*)?/

它不再匹配了。为什么?

现在,如果我让第一个变体匹配:

http://site.com

它提取:为第一个值(权限)和//site.com第二个值(路径)。

我没想到这会起作用,因为它没有路径并且路径未标记为可选。但仍然想知道这个结果,因为我只有这 2 个捕获组 -(.*?)(\/.*)

http://jsfiddle.net/U2tKT/1/

有人可以解释我怎么了。请不要链接到完整的 url 解析解决方案,我知道有很多,但我想了解我的正则表达式有什么问题(以及我如何解决这个问题)。

谢谢。

4

3 回答 3

3

user1436026 在我即将点击提交按钮之前发布了,但这里是:

您的域(权限)模式被标记为“不贪婪”,匹配尽可能少。在你的情况下,它实际上满足了完全不匹配的模式 - 这几乎是它得到的。相反,您想要的是尽可能地匹配域,直到您确定它所匹配的不再是域(我更改了正则表达式以匹配除 / 之外的任何内容,并且尽可能多地匹配。)

/^(?:http|https)?(?::\/\/)?(?:www\.)?([^\/]+)(\/.*)?/

我知道您特别声明您不希望任何指向 JS 中任何 URL 解析解决方案的链接,但是您知道 JS 已经内置了它吗?:)

var link = document.createElement('a');
link.href="http://www.site.com/part1/part2?key=value#blub";
auth=link.hostname; //www.site.com
path=link.pathname; // /part1/part2
于 2013-08-30T13:23:13.250 回答
2

在您的 regex 结束时/^(?:http|https)?(?::\/\/)?(?:www\.)?(.*?)(\/.*)?/(.*?)(因为它具有?修饰符,)试图尽可能地匹配以满足正则表达式。因为您已将正则表达式的最后一部分设为可选,所以(.*?)不必匹配任何内容即可满足正则表达式的其余部分,因为(\/.*)?允许不匹配任何内容。然而,当您将正则表达式的最后一部分设为强制时,(\/.*),(.*?)被强制匹配到足以(\/.*)使 匹配。

于 2013-08-30T13:21:01.677 回答
1

RFC3986

互联网工程任务组 ( IETF ) 的第 3986 号文档征求意见 (RFC) 标题为:“统一资源标识符 (URI):通用语法”( RFC3986 ),是描述构成有效的通用统一资源标识符 (URI)。附录 B介绍了您需要的正则表达式:

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

使用此正则表达式,URI 部分存储如下:

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

为了记录上面的正则表达式,我冒昧地用注释和缩进以自由间距模式重写它,并以经过测试的 PHP 脚本的形式呈现在这里,它解析出给定的所有主要部分URI 字符串:

PHP解决方案:

<?php // test.php Rev:20130830_0800

$re_rfc3986_parse_generic_uri = '%
    # Parse generic URI according to RFC3986 Appendix B.
    ^             # Anchor to start of string.
    (?:           # Group for optional scheme.
      ([^:/?#]+)  # $1: Uri SCHEME.
      :           # Scheme ends with ":".
    )?            # Scheme is optional.
    (?:           # Group for optional authority.
      //          # Authority starts with "//"
      ([^/?#]*)   # $2: Uri AUTHORITY.
    )?            # Authority is optional.
    ([^?#]*)      # $3: Uri PATH (required).
    (?:           # Group for optional query.
      \?          # Query starts with "?".
      ([^#]*)     # $4: Uri QUERY.
    )?            # Query is optional.
    (?:           # Group for optional fragment.
      \#          # Fragment starts with "#".
      (.*)        # $5: Uri FRAGMENT.
    )?            # Fragment is optional.
    $             # Anchor to end of string.
    %x';

$text = "http://www.site.com/part1/part2?key=value#blub";

if (preg_match($re_rfc3986_parse_generic_uri, $text, $matches)) {
    print_r($matches);
} else {
    echo("String is not a valid URI");
}
?>

对原始正则表达式进行了两个功能更改:1.) 将不必要的捕获组转换为非捕获,以及 2.)$在表达式末尾添加了字符串锚的结尾。请注意,可以通过使用命名捕获组而不是使用编号捕获组来创建更具可读性的版本,但不会直接转换为 JavaScript 语法。

PHP脚本输出:

Array
(
[0] => http://www.site.com/part1/part2?key=value#blub
[1] => http
[2] => www.site.com
[3] => /part1/part2
[4] => key=value
[5] => blub
)

JavaScript 解决方案:

这是一个经过测试的 JavaScript 函数,它将有效的 URI 分解为其各种组件:

// Parse a valid URI into its various parts per RFC3986.
function parseValidURI(text) {
    var uri_parts;
    var re_rfc3986_parse_generic_uri =
    /^(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?$/;
    // Use String.replace() with callback function to parse the URI.
    text.replace(re_rfc3986_parse_generic_uri,
        function(m0,m1,m2,m3,m4,m5) {
            uri_parts = {
                scheme      : m1,
                authority   : m2,
                path        : m3,
                query       : m4,
                fragment    : m5
            };
            return; // return value is not used.
        });
    return uri_parts;
}

undefined请注意,如果 URI 字符串中不存在,则返回对象的非路径属性可能存在。此外,如果 URI 字符串与此正则表达式不匹配(即明显无效),则返回值为undefined.

笔记:

  • 唯一需要的通用 URI 组件是路径(它本身可能是空的)。
  • 空字符串是有效的 URI!
  • 上面的正则表达式不验证 URI,而是解析给定的有效 URI。
  • 如果上述正则表达式无法匹配 URI 字符串,则该字符串不是有效的 URI。然而,反之则不成立——如果字符串确实匹配上面的正则表达式,这并不意味着 URI 是有效的,而只是意味着它可以作为一个 URI 进行解析。

对于那些对验证URI 并进一步分解感兴趣的人,我写了一篇文章,其中包含 RFC3986 附录 A 中定义的所有部分并将它们转换为正则表达式语法。看:

正则表达式 URI 验证

快乐的正则表达式!

于 2013-08-30T17:06:54.657 回答