💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# UTF-8:字符编码的秘密 [http://htmlpurifier.org/docs/enduser-utf8.html#findcharset](http://htmlpurifier.org/docs/enduser-utf8.html#findcharset) ## 寻找真实的编码 最初是ASCII,事情很简单。但是它们不好,因为没有人能用西里尔文或泰文写作。因此,爆炸了字符编码,以扩展ASCII可以表示的字符来解决该问题。字符编码历史的这个荒谬的简化版本向我们表明,现在有许多字符编码在浮动。 > 一个**字符编码**告诉计算机如何解释原始零和一成真实人物。通常通过将数字与字符配对来实现。 > > 周围有许多不同类型的字符编码,但是我们最常处理的是ASCII,8位编码和基于Unicode的编码。 > > * **ASCII**是基于英文字母的7位编码。 > * **8位编码**是ASCII的扩展,增加了有用的非标准字符(例如é和æ)的杂烩。他们只能添加127个字符,因此通常一次仅支持一个脚本。当您在网络上看到某个页面时,很可能会以其中一种编码方式对其进行编码。 > * **基于Unicode的编码**实现Unicode标准,包括UTF-8,UTF-16和UTF-32 / UCS-4。它们超越了8位,并且支持世界上几乎所有语言。作为网络的主要国际编码方式,UTF-8受到越来越多的关注。 我们旅程的第一步是找出您网站的编码。最可靠的方法是询问您的浏览器: 火狐浏览器 工具>页面信息:编码 IE浏览器 查看>编码:项目符号为非官方名称 Internet Explorer不会为您提供字符编码的MIME名称(即有用/真实),因此您必须使用其描述来查找它。一些常见的: | IE的描述 | Mime Name | | --- | --- | | 视窗 | | 阿拉伯语(Windows) | Windows-1256 | | 波罗的海(Windows) | Windows-1257 | | 中欧(Windows) | Windows-1250 | | 西里尔文(Windows) | Windows-1251 | | 希腊文(Windows) | Windows-1253 | | 希伯来语(Windows) | Windows-1255 | | 泰语(Windows) | TIS-620 | | 土耳其语(Windows) | Windows-1254 | | 越南文(Windows) | Windows-1258 | | 西欧(Windows) | Windows-1252 | | ISO标准 | | 阿拉伯文(ISO) | ISO-8859-6 | | 波罗的海(ISO) | ISO-8859-4 | | 中欧(ISO) | ISO-8859-2 | | 西里尔文(ISO) | ISO-8859-5 | | 爱沙尼亚语(ISO) | ISO-8859-13 | | 希腊文(ISO) | ISO-8859-7 | | 希伯来语(ISO逻辑) | ISO-8859-8-l | | 希伯来语(ISO-Visual) | ISO-8859-8 | | 拉丁文9(ISO) | ISO-8859-15 | | 土耳其文(ISO) | ISO-8859-9 | | 西欧(ISO) | ISO-8859-1 | | 其他 | | 简体中文(GB18030) | GB18030 | | 简体中文(GB2312) | GB2312 | | 简体中文(HZ) | 赫兹 | | 繁体中文(Big5) | 大5 | | 日语(Shift-JIS) | Shift\_JIS | | 日语(EUC) | EUC-JP | | 韩语 | EUC-KR | | Unicode(UTF-8) | UTF-8 | Internet Explorer无法识别某些较晦涩的字符编码,因此必须使用表查找实名是很麻烦的,因此,我建议使用Mozilla Firefox来查找您的字符编码。 ## 查找嵌入式编码 此时,您可能会问:“我们是否已经找到编码?”事实证明,Web开发人员可以在多个地方指定字符编码,并且在`META`标记中可以找到一个这样的地方: ~~~ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> ~~~ 您可以在`HEAD`HTML文档的部分中找到此内容。右边的文本`charset=`是“声明的”编码:HTML声称是这种编码,但是实际上是否如此取决于其他因素。现在,请注意您的`META`标签是否声明: 1. 字符编码与浏览器报告的字符编码相同, 2. 字符编码与浏览器的字符编码不同,或者 3. 根本没有`META`标签!(恐怖,恐怖!) ## 固定编码 这里给出的建议是将页面作为普通文本`text/html`。`application/xml`或必须使用不同的做法`application/xml+xhtml`,有关更多信息,请参见[XHTML媒体类型上的W3C文档](http://www.w3.org/TR/2002/NOTE-xhtml-media-types-20020430/)。 如果您的`META`编码和实际编码相符,请机灵!您可以跳过此部分。如果他们不... ### 没有嵌入式编码 在这种情况下,您需要在`META`网站中添加适当的标记。就像复制粘贴上面的代码片段并将UTF-8替换为实际编码的MIME名称一样简单。 > 对于所有那些怀疑者,有一个很好的理由为什么应该明确说明字符编码。当不告知浏览器文本的字符编码是什么时,它必须猜测:有时猜测是错误的。黑客可以操纵这种猜测,以使XSS越过过滤器,然后欺骗浏览器将其作为活动代码执行。[Google UTF-7漏洞](http://shiflett.org/archive/177)就是一个很好的例子。 > > `META`只要您的网络服务器发送正确的Content-Type标头,您就可以不用指定标签的字符编码就可以摆脱困境,但是为什么要冒险呢?此外,如果用户下载HTML文件,则不再有任何Web服务器来定义字符编码。 ### 嵌入式编码不同意 这是一个非常普遍的错误:另一个消息来源是告诉浏览器字符编码是什么,并且覆盖了嵌入式编码。该来源通常是网络服务器(即Apache)发送的Content-Type HTTP标头。与页面一起发送的通常的Content-Type标头可能如下所示: ~~~ 内容类型:text / html;字符集= ISO-8859-1 ~~~ 请注意,这里有一个charset参数:这是Web服务器告诉浏览器字符编码的方式,就像`META`我们之前接触过的标签一样。 > 实际上,对于`META`无法发送标头的上下文(例如,没有Web服务器的本地存储文件),该标签旨在代替HTTP标头。因此,名称`http-equiv`(等效于HTTP)。 解决此问题的方法有两种:更改`META`标签以匹配HTTP标头,或更改HTTP标头以匹配`META`标签。我们如何知道该怎么办?它取决于网站的内容:毕竟,标题和标签只是描述网页上实际字符的方式。 如果您的网站: ...仅使用ASCII字符, 两种方法都可以,但是我建议将两者都切换为UTF-8(稍后再介绍)。 ...使用特殊字符,它们可以正确显示, 将嵌入的编码更改为服务器编码。 ...使用特殊字符,但用户经常抱怨它们出现乱码, 将服务器编码更改为嵌入式编码。 更改META标签很容易:只需将旧编码换成新编码即可。但是,更改服务器(HTTP标头)编码会稍微困难一些。 ### 更改服务器编码 #### PHP header()函数 解决此问题的最简单方法是自己通过编程语言发送编码。由于您使用的是HTML Purifier,因此我假设使用PHP,尽管用[其他语言](http://www.w3.org/International/O-HTTP-charset#scripting)进行类似的操作并不难。合适的代码是: ~~~ 标头('Content-Type:text / html; charset = UTF-8'); ~~~ ...使用您的嵌入式编码替换UTF-8。此代码必须在任何输出之前出现,因此请注意应用程序中的空白(即,输出之前的任何空白,除了标记内的空白)。 #### PHP ini指令 PHP还有一个简洁的ini指令,可以节省您的标头调用:`[default_charset](http://php.net/ini.core#ini.default-charset)`。使用此代码: ~~~ ini_set('default_charset','UTF-8'); ~~~ ...也可以解决问题。如果PHP作为Apache模块运行(而不是作为FastCGI运行,请查阅[phpinfo](http://php.net/phpinfo)()了解详细信息),您甚至可以使用htaccess在多个PHP文件中应用此属性: ~~~ php_value default_charset“ UTF-8” ~~~ > 与所有INI指令一样,这也可以放在php.ini文件中。一些托管服务提供商允许您自定义自己的php.ini文件,要求您的支持以获取详细信息。采用: > > ~~~ > default_charset =“ utf-8” > ~~~ #### 非PHP 无论出于何种原因,您都可能需要在非PHP文件(通常是普通HTML文件)上设置字符编码。这样做更像是一个命中注定的过程:取决于用作网络服务器的软件和该软件的配置,某些技术可能有效或无效。 #### .htaccess 在Apache上,您可以使用.htaccess文件来更改字符编码。我将参考[W3C](http://www.w3.org/International/questions/qa-htaccess-charset)进行深入的解释,但是归结为创建一个名为.htaccess的文件,其内容如下: ~~~ AddCharset UTF-8 .html ~~~ 在将UTF-8替换为您要使用的字符编码的情况下,.html是将应用到的文件扩展名。然后,将直接在您放置此文件的目录中或该目录的子目录中为任何文件设置此字符编码。 如果您感到特别勇气,可以使用: ~~~ 添加默认字符集 UTF-8 ~~~ ...这将更改Apache添加到不具有任何Content-Type参数的任何文档中的字符集。出于安全原因,该指令(默认配置文件将其设置为iso-8859-1)可能是标题与`META`标签不匹配的原因。如果您不希望Apache不参与字符编码,则可以告诉它根本不发送任何内容: ~~~ AddDefaultCharset关 ~~~ ...使内部字符集声明(通常是`META`标签)成为字符编码信息的唯一来源。在这些情况下,*特别*重要的是要确保您`META`的页面上有有效的标记,并且之前的所有文本均为ASCII。 > 这些指令也可以放在Apache的httpd.conf文件中,但是在大多数共享托管情况下,您将无法编辑此文件。 #### 文件扩展名 如果不允许使用.htaccess文件,则通常可以背负Apache的默认AddCharset声明,以使文件具有适当的扩展名。这是Apache的默认字符集声明: | 字符集 | 文件扩展名 | | --- | --- | | ISO-8859-1 | .iso8859-1 .latin1 | | ISO-8859-2 | .iso8859-2 .latin2 .cen | | ISO-8859-3 | .iso8859-3 .latin3 | | ISO-8859-4 | .iso8859-4 .latin4 | | ISO-8859-5 | .iso8859-5 .latin5 .cyr .iso-ru | | ISO-8859-6 | .iso8859-6 .latin6 .arb | | ISO-8859-7 | .iso8859-7 .latin7 .grk | | ISO-8859-8 | .iso8859-8 .latin8 .heb | | ISO-8859-9 | .iso8859-9 .latin9 .trk | | ISO-2022-JP | .iso2022-jp .jis | | ISO-2022-KR | .iso2022-kr .kis | | ISO-2022-CN | .iso2022-cn。顺式 | | 大5 | .big5 .big5 .b5 | | WINDOWS-1251 | .cp-1251 .win-1251 | | CP866 | .cp866 | | KOI8-r | .koi8-r .koi8-ru | | KOI8-RU | .koi8-uk .ua | | ISO-10646-UCS-2 | .ucs2 | | ISO-10646-UCS-4 | .ucs4 | | UTF-8 | .utf8 | | GB2312 | .gb2312 .gb | | utf-7 | .utf7 | | EUC-TW | .euc-tw | | EUC-JP | .euc-jp | | EUC-KR | .euc-kr | | shift\_jis | .sjis | 因此,例如,一个名为`page.utf8.html`或的文件`page.html.utf8`可能会附加UTF-8字符集发送,不同之处在于,如果有`AddCharset charset .html`声明,它将覆盖.utf8扩展名`page.utf8.html`(优先级从右移到左)。默认情况下,Apache没有此类声明。 #### Microsoft IIS 如果有人可以提供有关如何配置Microsoft IIS来更改字符编码的信息,我将不胜感激。 ### XML格式 `META`标签是最常见的嵌入式编码来源,但它们也可以来自其他地方:XML声明。他们看起来像: ~~~ <?xml版本=“ 1.0”编码=“ UTF-8”?> ~~~ ...并且最常见于XML文档(包括XHTML)中。 对于XHTML,理论上,此XML声明会覆盖`META`标记。实际上,只有在XHTML实际上是合法的XML而不是HTML时才发生这种情况,这几乎永远不会由于Internet Explorer的缺乏而支持`application/xhtml+xml`(尽管通常认为这样做是一种[很好的做法,](http://www.hixie.ch/advocacy/xhtml)并且XHTML要求这样做)1.1规范)。 但是,对于XML,此XML声明非常重要。由于大多数Web服务器未配置为发送.xml文件的字符集,因此这是解析器唯一要做的事情。此外,XML文件的默认设置为UTF-8,它通常使用更常见的ISO-8859-1编码来对头(您在乱码的RSS提要中看到了这一点)。 简而言之,如果您使用XHTML并遇到了添加XML声明的麻烦,请确保它与您的`META`标签(仅在text / html中提供的标签)和HTTP标头配合使用。 ### 流程内部 本部分不是必读部分,但可能会回答您有关所有此字符编码轨迹的最新信息。如果您有兴趣进入下一个阶段,请跳过本节。 我们所有人都在研究并处理多种字符编码源之后,一个逻辑问题是“为什么会有这么多选择?”为了回答这个问题,我们必须回退字符编码的定义:它们允许程序将字节解释为人类可读的字符。 因此,出现了一个鸡蛋问题:必须使用字符编码来解释文档的文本。一个`META`标签是在文档的文本。该`META`标记给出了字符编码。`META`如果我们不知道标记的字符编码,如何确定其内容呢?如果我们不知道`META`标签的内容,我们如何找出字符编码? 对我们来说幸运的是,我们需要编写的字符`META`都是ASCII的,这在当今通常使用的每种字符编码中都非常普遍。因此,网络浏览器所需要做的就是一直解析直到到达Content-Type标记,提取字符编码标记,然后根据此新信息重新解析文档。 显然,这很复杂,因此浏览器更喜欢更简单,更有效的解决方案:从文档本身之外的其他位置(即HTTP标头)获取字符编码,这令无法设置这些标头的HTML作者非常恼火。 ## 为什么选择UTF-8? 因此,您已经经历了所有麻烦,以确保服务器和嵌入式字符都正确排列并存在。做得好:在这一点上,您可以知道自己的页面不容易受到字符编码样式XSS攻击的影响,从而退出并轻松自在。但是,就像拥有字符编码比完全没有字符编码要好一样,拥有UTF-8作为字符编码要比具有其他随机字符编码要好,下一步就是转换为UTF-8。但为什么? ### 国际化 许多软件项目在某一时刻突然意识到它们应该支持多种语言。即使是一种语言的常规用法,有时也需要偶尔使用特殊字符,这并不奇怪,您的字符集中没有该特殊字符。有时,开发人员通过添加对多种编码的支持来解决此问题:使用中文时,使用Big5,使用日语时,使用Shift-JIS,使用希腊语等。其他时候,他们使用带有热情的字符引用。 但是,UTF-8消除了对任何这些复杂措施的需要。使系统使用UTF-8并针对浏览器无法控制的源进行调整后(稍后会详细介绍),UTF-8才可以使用。您可以将它同时用于任何一种语言,甚至可以用于多种语言,您不必担心管理多种编码,也不必使用那些对用户不友好的实体。 ### 方便使用的 使用Latin-1(ISO-8859-1)编码的网站有时需要超出其范围的特殊字符,通常会使用字符实体引用来达到预期的效果。例如,可以写入θ`&theta;`,而与希腊字母的字符编码支持无关。 这对于限制使用特殊字符效果很好,但是说您想要这句话的中文文本:激光,这两个字是什么意思。与符号编码的版本如下所示: ~~~ &#28608;&#20809 ;,&#36889;&#20841;&#20491;&#23383;&#26159;&#29978;&#40636;&#24847;&#24605; ~~~ 对于我们当中那些真正知道角色实体是什么的人来说,这极为不便,而对于那些不知道这些角色的贫困用户来说,这是绝对无法理解的!甚至像稍微更用户友好的“难以理解”的字符实体,`&theta;`也会使对学习HTML不感兴趣的用户挠头。另一方面,如果他们在编辑框中看到θ,他们将知道这是一个特殊字符,并对其进行相应处理,即使他们自己不知道如何编写该字符也是如此。 > 对于最初使用ISO-8859-1的应用程序来说,Wikipedia是一个很好的案例研究,但是当它变得繁琐而无法支持外语时,便切换为UTF-8。Bot现在实际上将浏览文章并将字符实体转换为它们相应的真实字符,以便于用户友好和可搜索。有关更多详细信息,请参见[Meta的特殊字符页面](http://meta.wikimedia.org/wiki/Help:Special_characters)。 ### 形式 当我们站在用户的立场上时,非UTF-8网络表单如何处理其字符集之外的字符?除了要讨论UTF-8的正确之处,我们还要说明的是如果您不使用UTF-8并且人们尝试使用字符编码之外的字符,那么可能会出问题。 问题很大,范围广泛,而且很难解决(或者至少很难解决,如果您有时间和资源进行修复,那么最好迁移到UTF-8)。表单提交有两种类型:`application/x-www-form-urlencoded`一种用于GET,默认情况下用于POST,并且`multipart/form-data`可以由POST使用,并且在您要上传文件时需要。 以下是[提交和i18n](http://web.archive.org/web/20060427015200/ppewww.ph.gla.ac.uk/~flavell/charset/form-i18n.html)的注释摘要。该文档包含许多有用的信息,但是以粗俗的方式编写,因此在这里我尝试着重点。(注意:原始文件已经从网络上消失了,因此我链接到Web存档副本。)[`FORM`](http://web.archive.org/web/20060427015200/ppewww.ph.gla.ac.uk/~flavell/charset/form-i18n.html) #### `application/x-www-form-urlencoded` 这是GET请求必须使用的Content-Type,而POST请求默认使用。它涉及到无所不在的百分比编码格式,如下所示:`%C3%86`。由于百分比编码在字节级别上进行操作,因此没有确定这种请求的字符编码的官方方法,因此通常假定它与提交表单的页面的编码相同。([RFC 3986](http://tools.ietf.org/html/rfc3986#section-2.5)建议将文本标识符转换为UTF-8;但是,浏览器兼容性差强人意。)如果仅在选择的字符编码中使用字符,您将遇到很少的问题。 但是,一旦开始在编码之外添加字符(这比您想像的要普遍得多:以Microsoft的卷曲“ smart”引号为例),就会发生各种各样奇怪的事情。根据您使用的浏览器,它们可能: * 将不支持的字符替换为无用的问号, * 尝试修复字符(例如:将智能引号替换为常规引号), * 用字符实体引用替换字符,或者 * 无论如何,以不同的字符编码与原始编码混合发送(通常是Windows-1252,而不是散布在8位中的iso-8859-1或UTF-8) 为了适当地防止这些行为,您必须嗅探浏览器代理,编译具有不同行为的数据库,并对字符串采取适当的转换操作(而不管Internet Explorer每次出现的一系列极其神秘,随机和破坏性的错误)等一下)。或者您可以使用UTF-8并放心,因为UTF-8支持每个字符,所以这一切都不可能发生。 #### `multipart/form-data` 多部分表单提交消除了百分比编码所带来的许多歧义:服务器现在可以显式地请求某些编码,并且客户端可以在表单提交期间显式地告诉服务器字段的编码。 您可以通过两种方法使用此功能:将其保留为未设置状态,然后让浏览器以与页面相同的编码进行发送,或者将其设置为UTF-8,然后在服务器端执行另一次转换。每种方法都有缺陷,尤其是前者。 如果告诉浏览器以与页面相同的编码发送表单,则仍会遇到如何处理超出字符编码范围的字符的麻烦。行为再次有所不同:Firefox 2.0将它们转换为字符实体引用,而Internet Explorer 7.0则使它们难以理解。为了严肃的国际化目的,这不是一个选择。 另一种可能性是将Accept-Encoding设置为UTF-8,这引出了一个问题:您为什么不对所有内容都使用UTF-8?这条路线更可口,但是有一个值得注意的警告:您的数据将以UTF-8格式输入,因此您必须将其显式转换为您喜欢的本地字符编码。 我出于思想上的原因反对这种方法:当您本来可以转换为UTF-8时,您将更深入自己。而且,当然,您不能对GET请求使用此方法。 ### 很好的支持 如今,几乎所有狂野的现代浏览器都完全支持UTF-8和Unicode:麻烦的情况可以用一只手的手指来计算,这些浏览器通常也遇到其他字符编码的麻烦。用户通常会遇到的问题是由于缺少合适的字体来显示字符(再一次,这适用于所有字符编码和HTML实体)或Internet Explorer缺乏智能字体选择(可以解决)。 我们将在“迁移”部分中详细介绍如何处理浏览器世界中的一些极端情况,但是请放心,如果正确完成转换,转换为UTF-8不会导致用户困扰您破碎的页面。 ### HTML净化器 最后,我们进入HTML Purifier。HTML Purifier是为处理UTF-8而构建的:否则,任何指示都是编码器的结果,该编码器将文本从您的首选编码转换为UTF-8,然后再次转换。HTML Purifier永远不会触碰其他任何东西,而是将其留给模块iconv来完成肮脏的工作。 但是,这种方法并不完美。iconv根本不了解HTML字符实体。为了防止复杂的转义方案,HTML Purifier在处理文本之前将所有字符和数字实体引用标准化。这导致了一个重要的后果: **目标字符集不支持的任何字符,无论它是以字符实体引用还是原始字符的形式,都将被静默忽略。** 此原则的示例正在起作用:假设您已经`&theta;`在HTML中使用,但是输出使用的是Latin-1(可以理解,它不理解希腊语),则会发生以下过​​程(假设您已使用%Core正确设置了编码.Encoding): * 该`Encoder`会从ISO 8859-1转换文本为UTF-8(注意,theta是保存在这里,因为它实际上并未使用任何非ASCII字符):`&theta;` * 在`EntityParser`将改变所有命名和数字字符实体到他们相应的原始UTF-8当量:`θ` * HTML Purifier处理代码:`θ` * 在`Encoder`现在将来自UTF-8 ISO 8859-1背课文。由于ISO 8859-1不支持希腊语,因此它将被忽略或替换为问号:`?` 此行为是非常不令人满意的。它是国际应用程序的突破者,对于偶尔需要特殊字符的省级灵魂可能会有些恼火。从1.4.0版本开始,HTML Purifier使用%Core.EscapeNonASCIICharacters提供了稍微更可口的解决方法。现在,该过程如下所示: * 的`Encoder`编码为UTF-8变换:`&theta;` * 该`EntityParser`变换实体:`θ` * HTML Purifier处理代码:`θ` * 该`Encoder`替换数字实体引用的所有非ASCII字符:`&#952;` * 为了达到良好的效果,请将`Encoder`编码转换回原始`&#952;`格式(对于99%的编码来说,这绝对是不必要的):(请记住,全都是ASCII!) ...这意味着这仅对偶尔进入Unicode字符领域很有用,而中文或日文文本完全不可接受。更大的缺点是,假设输入编码实际上*是*支持theta的ISO-8859-7,*则*无论如何该字符都将转换为字符实体引用!(编码器不区分)。 当前的功能是关于HTML Purifier在永恒的岁月中所处的位置。HTML Purifier可以尝试保留字符引用的原始形式,以便可以将它们替换回去,只有DOM扩展名不可逆转地将其杀死。HTML Purifier也可以尝试变得聪明,只转换目标编码不支持的非ASCII字符,但这将需要重新实现具有HTML意识的iconv,而我不会这样做。 就是这样:要么是UTF-8,要么是残废的国际支持。您的选择!(而且我这里并不是在讽刺:有些人可能不太在乎其他语言)。 ## 迁移到UTF-8 因此,您已经决定硬着头皮,并想要迁移到UTF-8。请注意,这不是为胆小者准备的,您应该期望该过程花费的时间比您认为的要长。 通常的想法是,将所有现有文本转换为UTF-8,然后将我们前面讨论的所有标头和META标记设置为UTF-8。这样做的方法有很多:您可以编写一个转换脚本,该脚本在数据库中运行并将所有内容重新编码为UTF-8,或者当有人阅读该页面时,您可以即时进行转换。详细信息取决于您的系统,但是我将介绍一些可能会使您绊倒的更细微的迁移点。 ### 配置数据库 大多数现代数据库(最著名的开源数据库是MySQL 4.1+和PostgreSQL)都支持字符编码。从逻辑上讲,如果要切换到UTF-8,则需要确保数据库也知道该更改。但是有一些警告: #### 合法方法 用于指定字符编码的SQL语法方面的标准化是众所周知的。有关如何正确执行此操作,请参阅各自数据库的文档。 对于[MySQL](http://dev.mysql.com/doc/refman/5.0/en/charset-conversion.html),`ALTER`将神奇地为您执行字符编码转换。但是,您必须确保该列中的文字是它所说的:如果将Shift-JIS放在ISO 8859-1列中,则当您尝试将其转换为UTF-时,MySQL将不可逆转地处理该文本。 8。您必须将其转换为二进制字段,将其转换为Shift-JIS字段(实际编码),然后最终转换为UTF-8。许多网站的页面不可逆地受到破坏,因为他们没有意识到自己一直在欺骗字符编码;不要成为下一个受害者。 对于[PostgreSQL](http://www.postgresql.org/docs/8.2/static/multibyte.html),似乎没有直接的方法可以更改数据库的编码(从8.2版本开始)。您将必须转储数据,然后将其重新导入到新表中。确保正确设置了客户端编码:这就是PostgreSQL知道执行编码转换的方式。 很多时候,还会询问您新列的“排序规则”。排序规则是DBMS对文本进行排序的方式,例如将B,C和A排序为A,B和C(当您使用泰语和日语等语言时,问题变得异常复杂)。如有疑问,使用默认设置通常是一个安全的选择。 一旦完成所有的转换,您仍然必须记住使用`SET NAMES`(标准SQL,通常支持)在每个数据库连接上正确设置客户端编码(您的编码)。 #### 二元 由于上述兼容性问题,存储UTF-8文本的一种更可互操作的方法是将其填充为二进制数据类型。`CHAR`成为`BINARY`,`VARCHAR`成为`VARBINARY`,`TEXT`成为`BLOB`。这样做可以为您省去很多麻烦: * 二进制数据类型的语法非常易于移植, * MySQL 4.0*不*支持字符编码,因此,如果要支持字符编码,则*必须*使用二进制, * 从5.1版本开始,MySQL不支持四个字节的UTF-8字符,这些字符表示超出基本多语言平面的字符,并且 * 您将永远不必担心DBMS太聪明,并在不需要时尝试转换文本。 MediaWiki是一个非常著名的国际应用程序,由于第三点,它使用二进制字段来存储其数据。 当然也有缺点: * 由于PHPMyAdmin等数据库工具被声明为二进制文件,因此无法为您提供内联文本编辑功能, * 这在语义上是不正确的:它实际上是文本而不是二进制文件(位于数据库中), * 除非您使用上面提到的非便携式向导,否则您必须自己更改编码(通常,您会即时进行编码),并且 * 您将没有排序规则。 根据您的情况选择。 ### 文本编辑器 对于面向平面文件的系统,通常会负责将现有文本和HTML文件的一部分转换为UTF-8,并确保所有上传的新文件都经过正确编码。再一次,我只能模糊地指出转换现有文件的正确方向:确保备份,确保使用[iconv](http://php.net/ref.iconv)(),并确保知道文件的原始字符编码是(或取决于)在系统的整洁度上)。 但是,我可以针对文本编辑器提供更具体的建议。众所周知,许多文本编辑器都支持Unicode。要了解编辑器的工作方式,您可以查看[此列表](http://www.alanwood.net/unicode/utilities_editors.html)或[Wikipedia的列表。](http://en.wikipedia.org/wiki/Comparison_of_text_editors#Encoding_support)我个人使用的是Notepad ++,它在使用UTF-8时就像一个魅力。通常,您将必须通过一些对话(通常是“另存为”或“格式”)**明确**告诉编辑器您要使用的编码。编辑器通常会提供“ Unicode”作为保存方法,这是模棱两可的。确保您知道它们是否真正表示UTF-8或UTF-16(这是Unicode的另一种形式)。 需要注意的两件事是编辑器是否支持**字体混合**(一个文档中有多种字体)以及是否添加**BOM**。字体混合非常重要,因为字体很少支持人类已知的每种语言:为了灵活起见,编辑者必须能够从这里一点点拿走一点,否则所有的汉字都将变成漂亮的盒子。我们将在下面讨论BOM。 ### 字节顺序标记(标题已发送!) BOM([字节顺序标记](http://en.wikipedia.org/wiki/Byte_Order_Mark))是一个神奇的,不可见的字符,放置在UTF-8文件的开头,用于告诉人们编码是什么以及文本的字节序是什么。这也是不必要的。 因为它是不可见的,所以当它开始做本不应该做的事情时,常常会让人感到惊讶。例如,此PHP文件: ~~~ BOM <?php header('Location:index.php'); ?> ~~~ ...将会因所有过于熟悉的标**头已发送**PHP错误而失败。而且因为BOM是不可见的,所以这个罪魁祸首将不会被注意到。我的建议是仅在PHP页面中使用ASCII,但如果必须的话,请确保在不使用BOM表的情况下保存该页面。 > 错误所指的标头是**HTTP标头**,这些**标头**在发送任何HTML之前先发送给浏览器以告知各种信息。输出任何常规文本(是的,物料清单算作普通文本)后,必须发送标头,并且您再也不能发送了。因此,错误。 如果您正在阅读文本文件以插入到另一页的中间,强烈建议(但并非绝对必要)通过以下方式替换BOM的UTF-8字节序列`"\xEF\xBB\xBF"`: ~~~ $ text = str_replace(“ \ xEF \ xBB \ xBF”,``,$ text); ~~~ ### 字型 一般来说,遇到字体问题的人分为两类: * 那些想要使用一种非常晦涩难懂的语言的人,即使母语为母语的人也几乎没有这种支持,并且 * 那些文字的主要语言得到了很好的支持,但是偶尔不支持这些字符的语言。 是的,在Sinhalese网站上总是有英语使用者出现在网站上,并且字体不正确。但是,碰巧没有正确字体的英语用户可能仍然无法阅读僧伽罗语。因此,我们将处理其他两种情况。 #### 晦涩的脚本 如果您运行孟加拉语网站,则可能会得到那些希望阅读您的网站但收到大量问号或其他无意义字符的用户的评论。要解决此问题,需要安装字体或语言包,该字体或语言包通常高度依赖于语言。[这是](http://bn.wikipedia.org/wiki/%E0%A6%89%E0%A6%87%E0%A6%95%E0%A6%BF%E0%A6%AA%E0%A7%87%E0%A6%A1%E0%A6%BF%E0%A6%AF%E0%A6%BC%E0%A6%BE:Bangla_script_display_and_input_help)孟加拉语帮助文件[的示例](http://bn.wikipedia.org/wiki/%E0%A6%89%E0%A6%87%E0%A6%95%E0%A6%BF%E0%A6%AA%E0%A7%87%E0%A6%A1%E0%A6%BF%E0%A6%AF%E0%A6%BC%E0%A6%BE:Bangla_script_display_and_input_help)。我肯定那里也有其他人。您只需要将用户指向适当的帮助文件。 #### 偶尔使用 一个很好的例子,当您看到嵌入在非常平淡的ASCII中的一些非常晦涩的Unicode字符时,是[国际语音字母(IPA)的字母](http://en.wikipedia.org/wiki/International_Phonetic_Alphabet),用于以非常标准的方式指定发音(您可能会看到它们在字典中的时间)。您的平均字体可能不支持所有IPA字符,例如ʘ(双音)或ʒ(发声后齿擦音)。那么,浏览器性能不佳怎么办?字体混合!诸如Mozilla Firefox和Internet Explorer 7的智能浏览器将从其他字体中借用字形,以确保所有字符都能正确显示。 但是,如果浏览器不智能并且恰好是全世界使用最广泛的浏览器,会发生什么?当一个字符不存在时,Microsoft IE 6不够聪明,无法从其他字体中借用,因此通常您会被一个漂亮的大字符拍打。为了使一切正常,MSIE 6需要一些微调。您可以将其配置为使用其他字体来呈现文本,但是可以通过有选择地将特殊字符块的字体更改为已知的Unicode字体来达到相同的效果。 幸运的是,维基百科的人们已经为您完成了所有繁重的工作。从此处获取CSS:[Common.css](http://en.wikipedia.org/wiki/MediaWiki:Common.css),并搜索“ .IPA”。还有一些其他类可用于其他目的,请查看[此页面](http://meta.wikimedia.org/wiki/Help:Special_characters#Displaying_Special_Characters)以获取更多详细信息。对于那些懒惰的人,这应该可以工作: ~~~ .Unicode { 字体家族:Code2000,“ TITUS Cyber​​bit Basic”,“ Doulos SIL”, “ Chrysanthi Unicode”,“ Bitstream Cyber​​bit”, “ Bitstream Cyber​​Base”,Throomanes,Gentium,GentiumAlt, “ Lucida Grande”,“ Arial Unicode MS”,“ Microsoft Sans Serif”, “ Lucida Sans Unicode”; 字体家族/ ** /:inherit; / *为IE6以外的所有人重置字体* / } ~~~ 标准用法遵循`<span class="Unicode">Crazy Unicode stuff here</span>`。通常不需要修复[Windows字形列表](http://en.wikipedia.org/wiki/Windows_Glyph_List_4)中的字符,但是对于其他任何事情,您可能都希望安全播放。除非,当然,除非您不关心IE6用户。 ### 处理函数中的可变宽度 当人们声称PHP6将解决我们所有的Unicode问题时,他们被误导了。它不会解决任何上述问题。但是,它将解决我们将要讨论的问题:在PHP中处理UTF-8文本。 PHP(从PHP5开始)根本没有意识到UTF-8的存在(有一些值得注意的例外)。有时,这会引起问题,而其他时候则不会。到目前为止,我们避免讨论UTF-8的体系结构,因此,我们首先要问什么是UTF-8?是的,它支持Unicode,是的,它是可变宽度。其他特点: * 每个字符的字节序列都是唯一的,永远不会在另一个字符的字节序列内找到, * UTF-8最多可以使用四个字节来编码字符, * 必须检查UTF-8文本的格式是否正确, * 纯ASCII也是有效的UTF-8,并且 * 二进制排序将按照与Unicode相同的顺序对UTF-8进行排序。 这些特征中的每一个都以不同的方式影响文本处理的不同领域。解释这些含义的确切含义超出了本文档的范围。PHPWact提供了一个很好的[参考文档](http://www.phpwact.org/php/i18n/utf-8),说明了对每个功能的期望,尽管在某些方面涉及面很广。他们关于[字符集的](http://www.phpwact.org/php/i18n/charsets)更一般的注释也值得一看,以获取有关UTF-8的信息。处理Unicode文本时的一些经验法则: * 请勿使用以下功能: * ...转换大小写(strtolower,strtoupper,ucfirst,ucwords) * ...声称不区分大小写(str\_ireplace,stristr,strcasecmp) * 使用以下函数之前请三思: * ...计算字符(strlen将返回字节,而不是字符; str\_split和word\_wrap可能会损坏) * ...将字符转换为实体引用(UTF-8不需要实体) * ...执行非常复杂的字符串处理(\* printf) 注意:此列表仅适用于UTF-8编码的文本:如果您拥有100%确定是ASCII的字符串,请成为我的客人并使用`strtolower`(HTML Purifier使用此功能。) 无论如何,请始终以字节为单位,而不是以字符为单位。如果使用strpos()查找字符的位置,它将以字节为单位,但这通常无关紧要,因为substr()也可以使用字节索引操作! 您还需要确保您的UTF-8格式正确,并且可能需要替换其中一些功能。我建议使用Harry Fuecks的[PHP UTF-8](http://phputf8.sourceforge.net/)库,而不是直接使用mb\_string。HTML Purifier还定义了一些有用的UTF-8兼容功能:`Encoder.php`在`/library/HTMLPurifier/`目录中检出。 ## 进一步阅读 好吧,就是这样。希望该文档对了解UTF-8的工作原理起到了非常实用的跳板作用。您可能已经决定不迁移:很好,只知道输出将发生什么以及可能收到的错误报告。 许多其他开发人员已经讨论了Unicode,UTF-8和国际化的主题,我想请他们对字符集和编码进行更深入的研究。 * [绝对最低每一个软件开发人员绝对,积极必须知道的关于Unicode和字符集(没有借口!)](http://www.joelonsoftware.com/articles/Unicode.html)由Joel Spolsky的,提供了一个*非常*以Unicode和字符集在总体上是好的高水平的样子。 * [Wikipedia上](http://en.wikipedia.org/wiki/UTF-8)的UTF-8为UTF-8的内幕提供了许多有用的细节,尽管对于一开始不太了解Unicode的人来说可能有些反感。