Javascript的URL解析问题

URL parsing issue with Javascript

本文关键字:问题 URL Javascript      更新时间:2023-09-26

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

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

这适用于一个拥有一切的url,比如:

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

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

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

它不再匹配了。为什么?

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

http://site.com

它提取CCD_ 1作为第一值(权限)并且提取//site.com作为第二值(路径)。

我没想到这会起作用,因为它没有路径,路径也没有标记为可选。但我仍然想知道这个结果,因为我只有这两个捕捉组——(.*?)('/.*)

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

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

谢谢。

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

在正则表达式/^(?:http|https)?(?::'/'/)?(?:www'.)?(.*?)('/.*)?/的末尾,(.*?)(因为它有?修饰符)试图匹配尽可能小的,以满足正则表达式。因为您已经将正则表达式的最后一部分设置为可选,所以(.*?)不必为了满足正则表达式的其余部分而匹配任何内容,因为('/.*)?不允许匹配任何内容。然而,当您将正则表达式的最后一部分强制设置为('/.*)时,为了使:1匹配,(.*?)必须足够匹配。

RFC3986

互联网工程任务组(IETF)的征求意见(RFC)文件号3986,标题为:;统一资源标识符(URI):通用语法";(RFC3986)是描述组成有效通用统一资源标识符(URI)的所有组件的精确语法的权威标准。附录B提供了您需要的正则表达式:

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

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

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

为了记录上面的regex,我在自由间隔模式下用注释和缩进对其进行了重写,并以经过测试的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;
}

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

注意事项:

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

对于那些对验证URI并进一步分解它感兴趣的人,我写了一篇文章,它采用了RFC3986附录a中定义的所有部分,并将它们转换为regex语法。参见:

正则表达式URI验证

regexing快乐!