URL 编码 Unicode 字符的正确方法是啥?

Posted

技术标签:

【中文标题】URL 编码 Unicode 字符的正确方法是啥?【英文标题】:What is the proper way to URL encode Unicode characters?URL 编码 Unicode 字符的正确方法是什么? 【发布时间】:2010-10-29 02:35:44 【问题描述】:

我知道非标准的 %uxxxx 方案,但这似乎不是一个明智的选择,因为该方案已被 W3C 拒绝。

一些有趣的例子:

心脏字符。 如果我在浏览器中输入:

http://www.google.com/search?q=♥

然后复制粘贴,我看到了这个网址

http://www.google.com/search?q=%E2%99%A5

这看起来像是 Firefox(或 Safari)正在这样做。

urllib.quote_plus(x.encode("latin-1"))
'%E2%99%A5'

这是有道理的,除了不能用 Latin-1 编码的东西,比如三点字符。

如果我输入网址

http://www.google.com/search?q=…

到我的浏览器然后复制粘贴,我得到了

http://www.google.com/search?q=%E2%80%A6

返回。这似乎是这样做的结果

urllib.quote_plus(x.encode("utf-8"))

这是有道理的,因为……不能用 Latin-1 编码。

但我不清楚浏览器如何知道是使用 UTF-8 还是 Latin-1 解码。

因为这似乎是模棱两可的:

In [67]: u"…".encode('utf-8').decode('latin-1')
Out[67]: u'\xc3\xa2\xc2\x80\xc2\xa6'

有效,所以我不知道浏览器如何判断是使用 UTF-8 还是 Latin-1 解码。

我需要处理的特殊字符的正确做法是什么?

【问题讨论】:

您的两个示例都编码为 UTF-8。第一个肯定不是 Latin-1,因为它有三个字节长...... %E2%99%A5 是 the "black heart suit" in UTF-8 字节值的十六进制。那颗黑色的心不是Latin-1 character set 的一部分。 要可靠地查看浏览器的编码方式和内容(以及许多其他有用的信息),请使用大多数现代浏览器中内置的开发人员工具,或获取免费的 HTTP 调试器,如 Fiddler。 【参考方案1】:

我总是用 UTF-8 编码。来自Wikipedia page on percent encoding:

通用 URI 语法要求提供 URI 中字符数据表示的新 URI 方案实际上必须表示来自未保留集中的字符而无需翻译,并且应根据 UTF-8 将所有其他字符转换为字节,然后对这些值进行百分比编码。此要求于 2005 年 1 月随RFC 3986 的发布而引入。在此日期之前引入的 URI 方案不受影响。

似乎因为过去还有其他公认的 URL 编码方法,浏览器尝试了几种解码 URI 的方法,但如果你是编码的人,你应该使用 UTF-8。

【讨论】:

UTF-8 也应该使用,因为它是替代旧 URL 标准的新 IRI 标准(RFC 3987,tools.ietf.org/html/rfc3986)允许的唯一编码。 如果其他人和我一样惊讶,@RemyLebeau 的评论中的文本提到了 RFC3987,但链接是旧规范 3896。正确的 URL 显然是 tools.ietf.org/html/rfc3987 是的,很抱歉。 URI 由 RFC 3986 定义,IRI 由 RFC 3987 定义。【参考方案2】:

一般规则似乎是浏览器根据提供表单的页面的内容类型对表单响应进行编码。这是一个猜测,如果服务器向我们发送“text/xml; charset=iso-8859-1”,那么他们期望返回相同格式的响应。

如果您只是在 URL 栏中输入 URL,则浏览器没有可处理的基本页面,因此只能猜测。因此,在这种情况下,它似乎一直在使用 utf-8(因为您的两个输入都产生了三个八位字节形式的值)。

可悲的事实是,AFAIK 对于查询字符串中的值或 URL 中的任何字符应该被解释为什么字符集没有标准。至少在查询字符串中的值的情况下,没有理由假设它们一定确实对应于字符。

这是一个已知问题,您必须告诉服务器框架您希望查询字符串编码为哪个字符集——例如,在 Tomcat 中,您必须调用 request.setEncoding() (或一些类似的方法) 你调用任何 request.getParameter() 方法之前。缺乏关于这个主题的文档可能反映了许多开发人员对这个问题缺乏认识。 (我经常问 Java 的面试者 Reader 和 InputStream 的区别是什么,而且经常会一脸茫然)

【讨论】:

RFC 3987 (tools.ietf.org/html/rfc3986) 定义了一种标准编码 - 编码不允许未编码的字符时必须使用 UTF-8。【参考方案3】:

IRI (RFC 3987) 是取代 URI/URL(RFC 3986 及更早版本)标准的最新标准。 URI/URL 本身并不支持 Unicode(好吧,RFC 3986 添加了未来基于 URI/URL 的协议的规定以支持它,但不更新过去的 RFC)。 "%uXXXX" 方案是在某些情况下允许 Unicode 的非标准扩展,但并非所有人都普遍实施。另一方面,IRI 完全支持 Unicode,并且要求文本在进行百分比编码之前编码为 UTF-8。

【讨论】:

我希望看到对协议的更新,以便在 URL 中完全支持 unicode,而不仅仅是通过百分比编码。 IRI 允许未编码的 Unicode 字符,但保留字符必须编码的少数情况除外。【参考方案4】:

IRI 不会替换 URI,因为在某些情况下(包括 HTTP)只允许使用 URI(实际上是 ASCII)。

相反,您指定一个 IRI,它会在上网时转换为一个 URI。

【讨论】:

【参考方案5】:

第一个问题是您的需求是什么? UTF-8 编码是使用廉价编辑器创建的文本和支持多种语言之间的一个很好的折衷。关于识别编码的浏览器,响应(来自网络服务器)应该告诉浏览器编码。大多数浏览器仍然会尝试猜测,因为在很多情况下,这要么是缺失的,要么是错误的。他们通过读取一些结果流来猜测是否存在不适合默认编码的字符。目前所有浏览器(?我没有检查这个,但它非常接近真实)使用 utf-8 作为默认值。

所以请使用 utf-8,除非您有令人信服的理由使用许多其他编码方案之一。

【讨论】:

以上是关于URL 编码 Unicode 字符的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Unicode是啥???

java中的字符都是以Unicode编码?那么这里的字符指的是啥

unicode16与unicode32之间是啥转换关系

Grails 未正确编码 unicode 字符

Python文件处理里encoding和encode有事区别,bytes类型是啥意思?

如何将Wikipedia的Unicode编码转成百分号编码