进行完整解析的正则表达式相当可怕。我已经包含了命名反向引用以提高可读性,并将每个部分分成单独的行,但它仍然看起来像这样:
^(?:(?P<protocol>\w+(?=:\/\/))(?::\/\/))?
(?:(?P<host>(?:(?:&(?:amp|apos|gt|lt|nbsp|quot|bull|hellip|[lr][ds]quo|[mn]dash|permil|\
(?:(?P<path>(?:(?:&(?:amp|apos|gt|lt|nbsp|quot|bull|hellip|[lr][ds]quo|[mn]dash|permil|\
(?P<file>(?:(?:&(?:amp|apos|gt|lt|nbsp|quot|bull|hellip|[lr][ds]quo|[mn]dash|permil|\
(?:\?(?P<querystring>(?:(?:&(?:amp|apos|gt|lt|nbsp|quot|bull|hellip|[lr][ds]quo|[mn]dash|permil|\
(?:
需要冗长表述的原因是除了协议或端口,任何一部分都可能包含HTML实体,这使得对片段的划分非常棘手。因此,在最后几种情况下 - 主机、路径、文件、查询字符串和片段,我们允许任何HTML实体或任何不是
?
或
#
的字符。用于HTML实体的正则表达式如下:
$htmlentity = "&(?:amp|apos|gt|lt|nbsp|quot|bull|hellip|[lr][ds]quo|[mn]dash|permil|\#[1-9][0-9]{1,3}|[A-Za-z][0-9A-Za-z]+);"
当它被提取出来时(我使用了mustache语法来表示它),它变得更加易读:
^(?:(?P<protocol>(?:ht|f)tps?|\w+(?=:\/\/))(?::\/\/))?
(?:(?P<host>(?:{{htmlentity}}|[^\/?#:])+(?::(?P<port>[0-9]+))?)\/)?
(?:(?P<path>(?:{{htmlentity}}|[^?#])+)\/)?
(?P<file>(?:{{htmlentity}}|[^?#])+)
(?:\?(?P<querystring>(?:{{htmlentity}};|[^#])+))?
(?:#(?P<fragment>.*))?$
在JavaScript中,当然不能使用命名的反向引用,因此正则表达式变为:
^(?:(\w+(?=:\/\/))(?::\/\/))?(?:((?:(?:&(?:amp|apos|gt|lt|nbsp|quot|bull|hellip|[lr][ds]quo|[mn]dash|permil|\
在每一次匹配中,协议是\1,主机是\2,端口是\3,路径是\4,文件是\5,查询字符串是\6,片段是\7。