关于验证:哪些字符使URL无效?

Which characters make a URL invalid?

哪些字符使网址无效?

这些有效的网址是?

  • example.com/file[/].html
  • http://example.com/file[/].html


通常,RFC 3986定义的URI(请参阅第2节:字符)可以包含以下任何字符:

1
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=

请注意,此列表未说明URI中可能出现这些字符的位置。

任何其他字符都需要使用百分比编码(% hh)进行编码。 URI的每个部分对于需要由百分比编码的单词表示的字符有进一步的限制。


为了添加一些说明并直接解决上面的问题,有几类字符会导致URL和URI出现问题。

有些字符是不允许的,不应出现在URL / URI,保留字符(如下所述)中,以及在某些情况下可能导致问题的其他字符,但标记为"不明智"或"不安全"。 RFC-1738(URL)和RFC-2396(URI)中明确说明了字符受限制的原因。请注意,较新的RFC-3986(对RFC-1738的更新)定义了给定上下文中允许使用哪些字符的构造,但较旧的规范提供了对以下规则不允许哪些字符的更简单和更一般的描述。

URI语法中不允许排除的US-ASCII字符:

1
2
3
   control     = <US-ASCII coded characters 00-1F and 7F hexadecimal>
   space       = <US-ASCII coded character 20 hexadecimal>
   delims      ="<" |">" |"#" |"%" | <">

排除字符"#",因为它用于从片段标识符分隔URI。百分比字符"%"被排除,因为它用于转义字符的编码。换句话说,"#"和"%"是必须在特定上下文中使用的保留字符。

允许列出不明智的字符,但可能会导致问题:

1
   unwise      ="{" |"}" |"|" |"" |"^" |"[" |"]" |"`"

在查询组件中保留的字符和/或在URI / URL中具有特殊含义:

1
  reserved    =";" |"/" |"?" |":" |"@" |"&" |"=" |"+" |"$" |","

上面的"保留"语法类指的是URI中允许的那些字符,但在通用URI语法的特定组件中可能不允许这些字符。"保留"集中的字符不会在所有上下文中保留。例如,主机名可以包含可选的用户名,因此它可能类似于ftp://user@hostname/,其中"@"字符具有特殊含义。

以下是包含无效和不明智字符的网址示例(例如'$','[',']'),并且应正确编码:

1
http://mw1.google.com/mw-earth-vectordb/kml-samples/gp/seattle/gigapxl/$[level]/r$[y]_c$[x].jpg

URI / URL的一些字符限制是依赖于编程语言的。例如,'|' (0x7C)字符虽然在URI规范中仅标记为"不明智",但会在Java java.net.URI构造函数中抛出URISyntaxException,因此不允许使用http://api.google.com/q?exp=a|b之类的URL,如果使用Java,则必须将其编码为http://api.google.com/q?exp=a%7Cb使用URI对象实例。


这里的大多数现有答案都是不切实际的,因为它们完全忽略了地址的实际使用情况,例如:

  • https://en.wikipedia.org/wiki/M?bius_strip或
  • https://zh.wikipedia.org/wiki/Wikipedia:关于中文维基百科/ EN。
  • 首先,对术语的偏离?这些地址是什么?它们是有效的URL吗?

    从历史上看,答案是"不"。根据RFC 3986,从2005年开始,这样的地址不是URI(因此不是URL,因为URL是一种URI)。根据2005 IETF标准的术语,我们应该正确地称它们为RFC 3987中定义的IRI(国际化资源标识符),它们在技术上不是URI,但可以简单地通过对IRI中的所有非ASCII字符进行百分比编码来转换为URI 。

    根据现代规范,答案是肯定的。 WHATWG Living Standard简单地将以前称为"URI"或"IRI"的所有内容分类为"URL"。这使得所谓的术语与未阅读规范的普通人使用"URL"这个词的方式保持一致,这是该规范的目标之一。

    WHATWG生活标准允许哪些角色?

    根据"URL"这个更新的含义,允许使用哪些字符?在URL的许多部分,例如查询字符串和路径,我们被允许使用任意"URL单元",它们是

    URL code points and percent-encoded bytes.

    Ok.

    什么是"URL代码点"?

    The URL code points are ASCII alphanumeric, U+0021 (!), U+0024 ($), U+0026 (&), U+0027 ('), U+0028 LEFT PARENTHESIS, U+0029 RIGHT PARENTHESIS, U+002A (*), U+002B (+), U+002C (,), U+002D (-), U+002E (.), U+002F (/), U+003A (:), U+003B (;), U+003D (=), U+003F (?), U+0040 (@), U+005F (_), U+007E (~), and code points in the range U+00A0 to U+10FFFD, inclusive, excluding surrogates and noncharacters.

    Ok.

    (请注意,"URL代码点"列表不包括%,但如果它们是百分比编码序列的一部分,则"URL代码单元"中允许%。)

    我可以发现规范允许使用不在此集合中的任何字符的唯一位置是在主机中,其中IPv6地址包含在[]字符中。 URL中的其他任何位置都允许使用URL单元或一些限制性更强的字符集。

    旧RFC下允许哪些字符?

    为了历史,并且因为它没有在这里的答案中完全探索,所以让我们在旧的规范下进行检查。

    首先,我们有两种类型的RFC 3986保留字符:

  • :/?#[]@,它是RFC 3986中定义的URI的通用语法的一部分
  • !$&'()*+,;=,它不是RFC的通用语法的一部分,但保留用作特定URI方案的语法组件。例如,分号和逗号用作数据URI语法的一部分,&=用作查询字符串中普遍存在的?foo=bar&qux=baz格式的一部分(RFC 3986未指定)。
  • 上面的任何保留字符都可以在没有编码的URI中合法使用,既可以用于语法目的,也可以作为数据中的文字字符,在某些地方,这些使用不能被误解为服务于其语法目的的字符。 (例如,虽然/在URL中具有语法含义,但您可以在查询字符串中使用未编码的,因为它在查询字符串中没有意义。)

    RFC 3986还指定了一些未保留的字符,这些字符总是可以简单地用于表示没有任何编码的数据:

  • <5233>
  • 最后,允许%字符本身进行百分比编码。

    只留下以下禁止出现在URL中的ASCII字符:

  • 控制字符(字符0-1F和7F),包括换行符,制表符和回车符。
  • "<>\^`{|}
  • ASCII中的每个其他字符都可以合法地显示在URL中。

    然后RFC 3987使用以下unicode字符范围扩展该组未保留字符:

    1
    2
    3
    4
    5
    6
      %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
    / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
    / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
    / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
    / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
    / %xD0000-DFFFD / %xE1000-EFFFD

    鉴于最新的Unicode块定义,旧规范中的这些块选择看起来很奇怪和随意;这可能是因为在编写RFC 3987之后的十年中已经添加了块。

    最后,或许值得注意的是,仅仅知道哪些字符可以合法地出现在URL中是不足以识别某些给定字符串是否是合法URL,因为某些字符仅在URL的特定部分中是合法的。例如,保留字符[]在http:// [1080 :: 8:800:200C:417A] / foo等URL中作为IPv6文字主机的一部分是合法的,但不合法任何其他上下文,所以OP的http://example.com/file[/].html的例子是非法的。

    好。


    在您的补充问题中,您询问www.example.com/file[/].html是否是有效的URL。

    该URL无效,因为URL是一种URI,而有效URI必须具有http:之类的方案(请参阅RFC 3986)。

    如果您打算询问http://www.example.com/file[/].html是否是有效的URL,那么答案仍然是否定的,因为方括号字符在那里无效。

    方括号字符是为此格式的URL保留的:http://[2001:db8:85a3::8a2e:370:7334]/foo/bar(即IPv6文字而不是主机名)

    如果你想完全理解这个问题,那么值得仔细阅读RFC 3986。


    可以在URI中使用的所有有效字符(URL是一种URI)在RFC 3986中定义。

    所有其他字符都可以在URL中使用,前提是它们首先是"URL编码"。这涉及更改特定"代码"的无效字符(通常以百分号(%)后跟十六进制数字的形式)。

    此链接HTML URL编码参考包含无效字符的编码列表。


    一些Unicode字符范围是有效的HTML5,尽管使用它们可能仍然不是一个好主意。

    例如,href文档说http://www.w3.org/TR/html5/links.html#attr-hyperlink-href:

    The href attribute on a and area elements must have a value that is a valid URL potentially surrounded by spaces.

    然后,"有效网址"的定义指向http://url.spec.whatwg.org/,该网站的目的是:

    Align RFC 3986 and RFC 3987 with contemporary implementations and obsolete them in the process.

    该文档将URL代码点定义为:

    ASCII alphanumeric,"!","$","&","'","(",")","*","+",",","-",".","/",":",";","=","?","@","_","~", and code points in the ranges U+00A0 to U+D7FF, U+E000 to U+FDCF, U+FDF0 to U+FFFD, U+10000 to U+1FFFD, U+20000 to U+2FFFD, U+30000 to U+3FFFD, U+40000 to U+4FFFD, U+50000 to U+5FFFD, U+60000 to U+6FFFD, U+70000 to U+7FFFD, U+80000 to U+8FFFD, U+90000 to U+9FFFD, U+A0000 to U+AFFFD, U+B0000 to U+BFFFD, U+C0000 to U+CFFFD, U+D0000 to U+DFFFD, U+E1000 to U+EFFFD, U+F0000 to U+FFFFD, U+100000 to U+10FFFD.

    然后在语句中使用术语"URL代码点":

    If c is not a URL code point and not"%", parse error.

    在解析算法的几个部分,包括模式,权限,相对路径,查询和片段状态:所以基本上是整个URL。

    此外,验证器http://validator.w3.org/会传递"你好"之类的网址,并且不传递包含空格"a b"等字符的网址

    当然,正如Stephen C所提到的,它不仅仅是关于角色,还关系到背景:你必须要理解整个算法。但是由于类"URL代码点"用于算法的关键点,它可以很好地了解您可以使用或不使用的内容。

    另请参见:URL中的Unicode字符


    我需要选择字符来分割字符串中的URL,所以我决定创建自己无法在URL中找到的字符列表:

    1
    2
    3
    4
    5
    6
    >>> allowed ="-_.~!*'();:@&=+$,/?%#[]?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    >>> from string import printable
    >>> ''.join(set(printable).difference(set(allowed)))
    '`" <\x0b

    \x0c\\\t{^}|>'

    因此,可能的选择是换行符,制表符,空格,反斜杠和"<>{}^|。我想我会选择空格或换行符。 :)


    不是你的问题的答案,但验证网址真的是一个严重的p.i.t.a
    您可能只是更好地验证域名并留下URL的查询部分。这是我的经历。
    您也可以使用ping网址并查看它是否会产生有效的响应,但这对于这么简单的任务来说可能太多了。

    检测网址的正则表达式很丰富,google it :)


    我想出了一些PHP的正则表达式,它们将文本中的url转换为锚标签。 (首先它将所有www。网址转换为http://然后将所有网址转换为https?://到href = ... html链接

    $string = preg_replace('/(https?:\/\/)([!#$&-;=?\-\[\]_a-z~%]+)/sim', '$2',
    preg_replace('/(\s)((www\.)([!#$&-;=?\-\[\]_a-z~%]+))/sim', '$1http://$2', $string)
    );