打印

HR9001: 各浏览器对于字符编码别名支持的宽泛程度存在差异

作者:陆远

标准参考

根据 HTML4.01 规范中的描述,服务端应该提供给用户端文档的字符编码(character encoding)信息,最直接的方式为通过 HTTP 协议([RFC2616], 3.4 及 14.17) "Content-Type" 头字段的 "charset" 将文档的字符编码告诉用户端。例如以下 HTTP 头声明了字符编码为 ISO-8859-1:

Content-Type: text/html; charset=ISO-8859-1

处于某种情况无法访问服务器时,HTML 文档可以包含有关文档的字符编码的明确信息,META 元素可以用来为用户端提供这些信息。例如指定当前文档的字符编码为 ISO-8859-1,文档中应包含如下 META 声明:

<META http-equiv="Content-Type" content="text/html;
                charset=ISO-8859-1">

当 HTTP 协议与 META 元素均没有提供有关一个文档的字符编码信息时,HTML 还为一些元素提供了 charset 属性。结合这些机制,作者可以在很大程度上提高当用户获取资源时用户端识别字符编码的机会。

针对如何确定一个文档的字符编码,用户代码必须遵守下面的优先级顺序(优先级由高至低):

  1. HTTP "Content-Type" 字段中的 "charset" 参数。
  2. META 声明中 "http-equiv" 为 "Content-Type" 对应的值中的 "charset" 的值。
  3. 元素的 charset 属性。

关于 字符编码 的详细信息,请参考 HTML4.01 规范 5.2 Character encodings 以及 W3C Internationalization 关于 Character encodings 中的内容。

问题描述

各浏览器对于字符编码别名支持的宽泛程度有差异,当指定了浏览器无法识别的字符编码别名时,浏览器会以确定编码的优先级顺序采用设置的更低优先级的字符编码,以此类推。
而 Chrome Safari Opera 中对字符编码别名有着比其他浏览器更宽泛的支持。

造成的影响

若字符编码别名设置不当,则会造成页面在某些浏览器中出现文字编码错误,导致页面无法阅读。

受影响的浏览器

所有浏览器  

问题分析

我们通常情况下为页面设定的字符编码信息所指对应到浏览器内部大多是字符编码别名,如 ISO-8859-1。

首先分析当 HTTP "Content-Type" 头字段的 "charset" 参数与页面中 META 元素声明中 "http-equiv" 为 "Content-Type" 对应的值中的 "charset" 的值不同时各浏览器所采用的字符编码。

分析以下代码:charset.php

<?php header("Content-Type: text/html; charset=BIG5");
                ?> <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type"
                content="text/html; charset=UTF-8"/> </head>
                <body> <script> document.write((document.charset || document.characterSet).toUpperCase());
                </script> </body> </html>

上面是一段 PHP 代码,HTTP "Content-Type" 头字段设置了字符编码为 BIG5,页面中的 META 元素设置了字符编码为 UTF-8,页面本身的编码类型为 GB2312。页面执行时,通过脚本输出了当前浏览器所采用的字符编码类型。

这个动态页面在各浏览器中运行时均显示出了 BIG5,可见此时所有浏览器均遵照 HTML4.01 规范所述,以更高优先级的 HTTP "Content-Type" 头字段的 "charset" 参数的值作为字符编码类型。

关于 document.charset 和 document.characterSet 的详细信息,请参考 MSDN charset Property 与 MDC document.characterSet 中的内容。


在继续接下来的分析之前,先统计一下各浏览器对于没有任何字符编码设定的页面所采用的编码类型:default_charset.html

<!DOCTYPE html> <html> <head> </head> <body> <script>
                document.write((document.charset || document.characterSet).toUpperCase()); </script> </body>
                </html>

上面页面中没有设定任何的字符编码信息,则各浏览器对于这个页面将使用各自的默认编码1。页面自身的编码为 GB2312

各浏览器中运行效果如下:

IE6 IE7 IE8 Firefox Chrome Safari Opera
字符编码 --- GB2312 ×Ö·û±àÂë --- ISO-8859-1 字符编码 --- GBK

可以看到各浏览器对页面的默认字符编码不尽相同。

当页面没有设置任何字符编码信息或者浏览器无法识别 HTTP 头字段以及 META 元素中所声明的字符编码信息时,所有浏览器均会以各自在当前语言版本下的默认字符编码显示页面。
本例中,因为页面自身的编码为 GB2312,则 Windows 平台下 IE Firefox Opera 简体中文版的默认字符编码刚好为 GB2312,所以页面中的字符显示正常。

注 1:操作系统及浏览器语言均为简体中文。


下面看一组特殊的例子:charset-x.php

<?php header("Content-Type: text/html; charset=maccyrillic"); ?> <!DOCTYPE HTML>
                <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=b.i.g+5"/>
                </head> <body style="font:24px Tahoma;"> 字符編碼 --- <script>
                document.write((document.charset || document.characterSet).toUpperCase()); </script> </body>
                </html>

上面的动态页面自身的编码为 BIG5,HTTP "Content-Type" 头字段设置了字符编码为 maccyrillic,页面中的 META 元素设置了字符编码为 b.i.g+5

各浏览器中运行效果如下:

IE6 IE7 IE8 Firefox Chrome Safari Opera
才絪絏 --- GB2312 ¶r≤≈љsљX --- X-MAC-CYRILLIC 字符編碼 --- BIG5
  • IE6 IE7 IE8 Firefox 中,浏览器无法识别 maccyrillic 这种字符编码别名,所以寻找更低优先级的 META 元素声明的字符编码,但发现也无法识别 b.i.g+5 这种字符编码别名,继而采用了当前语言版本的默认编码 GB2312,与页面自身的字符编码 BIG5 不相符,导致页面中的文字显示异常。
  • Chrome Safari 中,浏览器将 maccyrillic 识别为合法的 X-MAC-CYRILLIC,则不再理会更低优先级的编码设置,页面采用 X-MAC-CYRILLIC 作为字符的编码,与页面自身的 BIG5 编码不符,导致页面中的文字显示异常。
  • Opera 中,浏览器无法识别 maccyrillic 这种字符编码别名,所以寻找更低优先级的 META 元素声明的字符编码,将 b.i.g+5 这种字符编码别名识别为 BIG5,刚好与页面自身的 BIG5 字符编码相符,所以页面中的文字显示正常。

出现上述现象的原因主要有三点:

  1. 各浏览器的字符编码别名表不尽相同,对同一种编码下的各种别名支持的宽泛程度不一样。像 maccyrillic 这种别名在 Chrome Safari 可以识别为通用的 X-MAC-CYRILLIC1,但其他浏览器则会将其视作错误的字符编码信息处理。
  2. 各浏览器在匹配页面的字符编码与别名表中的字符编码时,匹配的方式不同。Chrome Safari Opera 会将编码类型的字符串做一个转换,过滤了除英文大小写字符、数字字符之外的字符(isASCIIAlphanumeric)。如 ISO8859_5 会以转换后的 ISO88595 与别名表中的 ISO-8859-5 转换后的 ISO88595 做比较,b.i.g+5 也会转换为 big5 与别名表中的做比较,所以浏览器可以正确识别这些设置的字符编码为浏览器内部的别名。
  3. 各浏览器的默认字符编码不同。

注 1:各浏览器均可以识别 X-MAC-CYRILLIC 这种通用的字符编码别名。

解决方案

首先,对于动态页面必须确保 HTTP "Content-Type" 头字段的 "charset" 参数与页面自身编码相符,且务必在页面的 META 元素中也声明相符的字符编码信息。对于静态页面,必须保证页面中 META 元素声明中 "http-equiv" 为 "Content-Type" 对应的值中的 "charset" 的值与页面自身编码相符。
其次,在设置字符编码别名时,最好使用最通用的、各浏览器均可识别的编码别名。

下面列出了部分的字符编码及其推荐的通用的别名在各浏览器中的支持情况:

标准字符编码名 推荐的字符编码别名
名称 各浏览器测试 名称 各浏览器测试
IE6 IE7 IE8 Firefox Chrome Safari Opera IE6 IE7 IE8 Firefox Chrome Safari Opera
ANSI_X3.4-1968 US-ASCII US-ASCII US-ASCII WINDOWS-1252 US-ASCII US-ASCII US-ASCII US-ASCII WINDOWS-1252
ISO_8859-1:1987 ISO-8859-1 不识别 ISO-8859-1 WINDOWS-1252 ISO-8859-1 ISO-8859-1 ISO-8859-1 ISO-8859-1 WINDOWS-1252
ISO_8859-2:1987 ISO-8859-2 不识别 ISO-8859-2 ISO-8859-2 ISO-8859-2 ISO-8859-2 ISO-8859-2 ISO-8859-2 ISO-8859-2
ISO_8859-3:1988 ISO-8859-3 不识别 ISO-8859-3 ISO-8859-3 ISO-8859-3 ISO-8859-3 ISO-8859-3 ISO-8859-3 ISO-8859-3
ISO_8859-4:1988 ISO-8859-4 不识别 ISO-8859-4 ISO-8859-4 ISO-8859-4 ISO-8859-4 ISO-8859-4 ISO-8859-4 ISO-8859-4
ISO_8859-5:1988 ISO-8859-5 不识别 ISO-8859-5 ISO-8859-5 ISO-8859-5 ISO-8859-5 ISO-8859-5 ISO-8859-5 ISO-8859-5
ISO_8859-6:1987 ISO-8859-6 不识别 ISO-8859-6 ISO-8859-6 ISO-8859-6 ISO-8859-6 ISO-8859-6 ISO-8859-6 ISO-8859-6
ISO_8859-7:1987 ISO-8859-7 不识别 ISO-8859-7 ISO-8859-7 ISO-8859-7 ISO-8859-7 ISO-8859-7 ISO-8859-7 ISO-8859-7
ISO_8859-8:1988 ISO-8859-8 不识别 ISO-8859-8 ISO-8859-8 ISO-8859-8 ISO-8859-8 ISO-8859-8 ISO-8859-8 ISO-8859-8
ISO_8859-9:1989 ISO-8859-9 不识别 WINDOWS-1254 ISO-8859-9 ISO-8859-9 ISO-8859-9 ISO-8859-9 WINDOWS-1254 ISO-8859-9
ISO-8859-10 不识别 ISO-8859-10 ISO-8859-10 ISO-8859-10 ISO-8859-10 不识别 ISO-8859-10 ISO-8859-10 ISO-8859-10

更多请参见 IANA CHARACTER SETSRFC1345

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.8
Chrome 6.0.472.25 dev
Safari 5.0.1
Opera 10.60
测试页面: charset.php
default_charset.html
charset-x.php
本文更新时间: 2010-08-12

关键字

content-type meta charset 字符集 元数据 编码