非 ASCII 字符在 URL 中的处理方式

9
我遇到了一个之前从未见过的新问题:我的客户正在向我们建立的项目中添加文件,其中一些文件名包含特殊字符,因为其中一些单词是西班牙语。
例如,我正在测试的一个文件中有一个á。我在CSS文件中调用该图像作为背景图像,但在Safari中它不显示。但在FF和Chrome上可以显示。
作为测试,我将链接粘贴到浏览器中,结果相同。在FF和Chrome上可以正常工作,但Safari会抛出错误。所以这些语言字符可能会导致问题?
Firefox会转换以下URL,并将á更改为a%CC%81并加载图像。

http://www.themediacouncil.com/test/nonascii/LA-MAR_Cebiche-Clássico_foto-Henrique-Peron-470x120-1371827671.jpg

您可以看到它在上方中断...但是FF和Chrome将其转换为:http://www.themediacouncil.com/test/nonascii/LA-MAR_Cebiche-Cla%CC%81ssico_foto-Henrique-Peron-470x120-1371827671.jpg

您也可以在此处查看实际效果:http://jsfiddle.net/Md4gZ/2/

.testbox { width:340px; height:100px; background:url('http://www.themediacouncil.com/test/nonascii/LA-MAR_Cebiche-Clássico_foto-Henrique-Peron-470x120-1371827671.jpg') no-repeat top left; }

那么正确的处理方式是什么。我正在使用PHP和WORDPRESS进行开发。我宁愿不要告诉客户端返回并替换所有具有特殊字符的文件。

感谢您的帮助。

2个回答

11

我相信现在的标准是将非ASCII字符转换为UTF-8字节序列,并将这些序列作为URL中的%HH十六进制代码包含其中。字符á是U+00E1(Unicode),在UTF-8中,它变为两个字节0xC3 0xA1。因此,Clássico会变成Cl%C3%A1ssico

Firefox所报告的转换结果Cla%CC%81ssico略有不同:它将á更改为a后跟U+0301,也就是组合重音符号(COMBINING ACUTE ACCENT字符)。在UTF-8中,U+0301变为0xCC 0x81

选择使用unicode“á”还是“后接组合重音符”的表示取决于Web服务器需要匹配的内容。在你的情况下,也许文件名实际上包含具有组合重音符的字符,这就是它可以工作的原因(很难说)。

处理非ASCII拉丁字符的另一种较旧的方法是使用8位拉丁字符集表示(如ISO-8859-1或类似的Windows-1252),并将其编码为一个字节。这将使Clássico变成Cl%E1ssico。但由于这仅适用于拉丁字符集,并对其中某些字符有歧义,因此它有望消失。


你建议在头部声明,这样浏览器可以自行转换字符,还是使用某种脚本?我和原帖作者(WordPress)的设置相同。 - Robert Sinclair
1
我无法针对任何特定设置进行说明,但总的来说,我建议在代码中(在 HTML 或其他地方)使用与 Web 服务器相同的约定编码 URL。如果您可以影响 Web 服务器所使用的约定,我建议使用 UTF-8 字节序列,其中使用%HH。在头文件中声明吗?不确定是否会有任何影响,可能取决于浏览器。 - njlarsson

0

@njlarsson已经很好地解释了该怎么做:

你从Firefox,Cla%CC%81ssico报告的转换略有不同:它将á改为a后跟U+0301,即COMBINING ACUTE ACCENT字符。在UTF-8中,U+0301变成0xCC 0x81。

更一般地说,我想知道为什么以及如何正确,以下是我的思考。

为什么会有这样的动机?

除了原始动机之外 - 西班牙用户不需要知道任何关于编码或解码的东西(除非他们是工程师或开发人员负责修复损坏的实现),另一个例子可以在Google JavaScript风格指南中找到,它适用于所有编程语言:

提示:永远不要仅仅因为担心某些程序可能无法正确处理非ASCII字符而使您的代码难以阅读。如果发生这种情况,那些程序就是错误的,必须修复

从高层次来看,在URL中使用百分号%编码与IETF RFC 1738 Section 2.2保持一致。注意它没有说明%编码的含义,但是根据惯例,网络采用UTF-8,这可以从2013年Firefox和Chrome的正确行为中看出。

在PHP(因此在WordPress中)出现问题的地方是文件名字符串很可能没有以UTF-8进行编码。这可能会是一个自然的问题?

编码、解码和重新编码

该字符串最初可以以UTF-8编码形式提供,然后解码为某种内部格式,例如UCS-2LE(这可以使某些字符串操作更快,但在其他方面会失败,例如表情符号,因为它们被编码在基本多语言平面之外),然后重新编码为打印为UTF-8。

在 PHP 中继续,例如使用 mb_convert_encoding,这可能需要 php-cli 或服务器已安装 php-mbstring
php > $encoded = "http://www.themediacouncil.com/test/nonascii/LA-MAR_Cebiche-Cla%CC%81ssico_foto-Henrique-Peron-470x120-1371827671.jpg";
php > $decoded = mb_convert_encoding($encoded, "UTF-8", "UCS-2LE");
php > $reencoded = mb_convert_encoding($decoded, "UCS-2LE", "UTF-8");
php > echo $reencoded;
http://www.themediacouncil.com/test/nonascii/LA-MAR_Cebiche-Cla%CC%81ssico_foto-Henrique-Peron-470x120-1371827671.jpg

或者该字符串最初根本没有以UTF-8编码,这将取决于它来自哪里等因素,这些因素在此处未提供。

另外:如果天真地打印$decoded字符串,它很可能是无意义的 - 这看起来有点像Python 2“乱码”问题

php > echo $decoded;  # UCS-2LE printed naively likely shows nonsense
瑨灴⼺眯睷琮敨敭楤捡畯据汩挮浯琯獥⽴潮慮捳楩䰯ⵁ䅍归敃楢档ⵥ汃╡䍃㠥猱楳潣晟瑯ⵯ效牮煩敵倭牥湯㐭〷ㅸ〲ㄭ㜳㠱㜲㜶⸱灪?

如何执行UTF-8转换?

关于计算机如何将数据以二进制或十六进制形式物理表示的确切低级细节和数学知识,假设有人对此足够好奇,可以在StackOverflow上找到。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接