当前位置:  开发笔记 > 编程语言 > 正文

UTF-8一路走来

如何解决《UTF-8一路走来》经验,为你挑选了12个好方法。

我正在设置一个新服务器,并希望在我的Web应用程序中完全支持UTF-8.我过去曾在现有的服务器上尝试过此操作,但最终似乎不得不回归ISO-8859-1.

我在哪里需要设置编码/字符集?我知道我需要配置Apache,MySQL和PHP来执行此操作 - 是否有一些我可以遵循的标准清单,或者可能是在出现不匹配的地方进行故障排除?

这适用于运行MySQL 5,PHP,5和Apache 2的新Linux服务器.



1> chazomaticus..:

数据存储:

utf8mb4在数据库的所有表和文本列中指定字符集.这使得MySQL物理存储和检索以UTF-8本地编码的值.请注意,utf8mb4如果utf8mb4_*指定了排序规则,MySQL将隐式使用编码(没有任何显式字符集).

在旧版本的MySQL(<5.5.3)中,不幸的是,您将被迫使用简单utf8,只支持Unicode字符的子集.我希望我在开玩笑.

数据访问:

在您的应用程序代码(例如PHP)中,无论您使用何种数据库访问方法,都需要将连接字符集设置为utf8mb4.这样,当MySQL将数据移交给您的应用程序时,MySQL不会从其原生UTF-8进行转换,反之亦然.

一些驱动程序提供了自己的配置连接字符集的机制,它们都更新了自己的内部状态,并通知MySQL要在连接上使用的编码 - 这通常是首选方法.在PHP中:

如果您使用PHP≥5.3.6的PDO抽象层,则可以charset在DSN中指定:

$dbh = new PDO('mysql:charset=utf8mb4');

如果您使用的是mysqli,可以致电set_charset():

$mysqli->set_charset('utf8mb4');       // object oriented style
mysqli_set_charset($link, 'utf8mb4');  // procedural style

如果您遇到普通的mysql但碰巧运行PHP≥5.2.3,则可以调用mysql_set_charset.

如果驱动程序没有提供自己的设置连接字符集的机制,则可能必须发出一个查询来告诉MySQL应用程序如何期望连接上的数据被编码:SET NAMES 'utf8mb4'.

与上述相同的考虑utf8mb4/ utf8适用.

输出:

如果您的应用程序将文本传输到其他系统,则还需要告知它们字符编码.对于Web应用程序,必须通知浏览器发送数据的编码(通过HTTP响应头或HTML元数据).

在PHP中,您可以使用default_charsetphp.ini选项,或者Content-Type自己手动发出MIME头,这只是更多工作但具有相同的效果.

输入:

不幸的是,在尝试存储或在任何地方使用它之前,您应该将每个收到的字符串验证为有效的UTF-8.PHP json_encode()诀窍,但你必须虔诚地使用它.真的没办法解决这个问题,因为恶意客户端可以用他们想要的任何编码提交数据,而且我还没有找到让PHP可靠地为你做这件事的技巧.

从我对当前HTML规范的阅读中,对于现代HTML,以下子项目不再是必需的,甚至不再有效.我的理解是浏览器将使用为文档指定的字符集中的数据并提交数据.但是,如果您要定位旧版本的HTML(XHTML,HTML4等),这些点可能仍然有用:

对于HTML5之前的HTML:您希望浏览器发送给您的所有数据都是UTF-8.不幸的是,如果你顺利地做到这一点的唯一方法是将JSON_UNESCAPED_UNICODE属性添加到你的所有mb_check_encoding()标签:accept-charset.

仅适用于HTML5之前的HTML:请注意,W3C HTML规范说客户端"应该"默认在服务器所服务的任何字符集中将表单发送回服务器,但这显然只是一个建议,因此需要在每个单独显示

标签.

其他代码注意事项:

显然,你要服务的所有文件(PHP,HTML,JavaScript等)都应该用有效的UTF-8编码.

您需要确保每次处理UTF-8字符串时都安全地执行此操作.不幸的是,这是困难的部分.您可能希望广泛使用PHP的扩展.

PHP的内置字符串操作默认情况下不是 UTF-8安全的. 对于普通的PHP字符串操作(如连接),您可以安全地执行某些操作,但对于大多数情况,您应该使用等效函数.

要知道你在做什么(阅读:不要搞砸了),你真的需要知道UTF-8以及它如何在尽可能低的水平上运行.查看来自utf8.com的任何链接,获取一些很好的资源,以了解您需要了解的所有信息.


请注意,MySQL与其他人的语言不同.当MySQL说"utf8"时,它真的意味着"一些奇怪的UTF-8变种,限于三个字节,因为上帝知道什么是荒谬的理由".如果你真的想要UTF-8,你应该告诉MySQL你想要这个奇怪的东西MySQL喜欢调用[utf8mb4](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html ).不要在"WTF!"上节省费用.
我没错:COLLATE意味着CHARACTER SET.参见例如http://dev.mysql.com/doc/refman/5.0/en/charset-database.html.
考虑添加PDO示例以设置字符集.
我的理解是,如果您将排序规则指定为utf8_*,它也会自动编码为utf8.这是错的吗?
这个答案对我帮助很大,但我也发现在我的情况下,我需要在通过ajax传回DB查询结果时将JSON_UNESCAPED_UNICODE添加到我的PHP json_encode中.
UTF-8可以编码所有Unicode字符.它只有MySQL的UTF-8限制为3个字符.
整理与编码不同.请务必在数据库中将编码设置为utf8.整理不太重要.
相关说明:`utf8mb4`表键不超过250个字符,因为MySQL限制为1000个字节.所以,不再是`VARCHAR(255)`(对于键,至少).

2> mercator..:

我想在chazomaticus的优秀答案中添加一点:

不要忘记META标记(像这样,或HTML4或XHTML版本):


这似乎微不足道,但IE7之前给我带来了问题.

我做的一切都很正确; 数据库,数据库连接和Content-Type HTTP标头都设置为UTF-8,并且在所有其他浏览器中都运行良好,但Internet Explorer仍然坚持使用"西欧"编码.

事实证明该页面缺少META标签.添加即可解决问题.

编辑:

W3C实际上有一个相当大的部分致力于I18N.他们有很多与此问题相关的文章 - 描述HTTP,(X)HTML和CSS方面:

FAQ:将(X)HTML页面编码更改为UTF-8

在HTML中声明字符编码

教程:XHTML,HTML和CSS中的字符集和编码

设置HTTP charset参数

他们建议同时使用HTTP标头和HTML元标记(或者在XHTML作为XML的情况下使用XML声明).


另外,确保该行是head元素的第一个子元素(在任何Unicode之前).浏览器可以在点击上述元元素之后重新解释页面.
@oliver:是的,您可以在HTTP标头中发送它,但最好将其发送到内容中,因为如果客户端保存文件,它将始终保存元标记.除非浏览器足够智能以将其复制到已保存文件中的元标记中,否则HTTP标头很可能会消失.

3> chroder..:

除了default_charset在php.ini中设置外,您还可以header()在任何输出之前使用from代码发送正确的字符集:

header('Content-Type: text/html; charset=utf-8');

只要您意识到大多数字符串函数不能与Unicode一起使用,并且有些函数可能会完全破坏字符串,那么在PHP中使用Unicode很容易.PHP认为"字符"长度为1个字节.有时这是可以的(例如,explode()只查找一个字节序列并将其用作分隔符 - 因此,您查找的实际字符无关紧要).但有时候,当函数实际设计用于处理字符时,PHP不知道你的文本有多字节字符,可以用Unicode找到.

一个很好的图书馆是phputf8.这会重写所有"坏"函数,以便您可以安全地处理UTF8字符串.有像mbstring扩展这样的扩展试图为你做这个,但我更喜欢使用库,因为它更便携(但我写大众市场的产品,所以这对我很重要).但是,无论如何,phputf8可以在幕后使用mbstring来提高性能.



4> Jim W...:

老话题,我知道.发现某人使用PDO的问题,答案是将其用于PDO连接字符串:

$pdo = new PDO(
    'mysql:host=mysql.example.com;dbname=example_db',
    "username",
    "password",
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

我从这个网站上下来的网站,幸运地使用谷歌缓存得到它.



5> JDelage..:

就我而言,我正在使用mb_split,它使用正则表达式.因此,我还必须手动确保正则表达式编码是utf-8mb_regex_encoding('UTF-8');

作为旁注,我还通过运行发现mb_internal_encoding()内部编码不是utf-8,我通过运行改变了它mb_internal_encoding("UTF-8");.



6> Jimmy Kane..:

首先,如果你在<5.3PHP,那么没有.你有很多问题需要解决.

我很惊讶没有人提到过intl库,它对unicode,字形,字符串操作,本地化等有很好的支持,见下文.

我将在PHPBenelux'14的Elizabeth Smith的 幻灯片中引用一些关于PHP中的unicode支持的信息

INTL

好:

ICU图书馆周围的包装

标准化语言环境,为每个脚本设置区域设置

数字格式

货币格式

消息格式化(替换gettext)

日历,日期,时区和时间

Transliterator

Spoofchecker

资源包

转换器

IDN支持

字形

整理

迭代器

坏:

不支持zend_multibite

不支持HTTP输入输出转换

不支持函数重载

mb_string

启用zend_multibyte支持

支持透明的HTTP输入/输出编码

为funtionallity提供一些包装器,例如strtoupper

ICONV

主要用于字符集转换

输出缓冲处理程序

mime编码功能

转变

一些字符串助手(len,substr,strpos,strrpos)

流过滤器 stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

DATABASES

mysql:表和连接上的字符集和排序规则(不是排序规则).也不要使用mysql - msqli或PDO

postgresql:pg_set_client_encoding

sqlite(3):确保它是使用unicode和intl支持编译的

其他一些问题

除非使用第3部分扩展,否则不能将unicode文件名与PHP和Windows一起使用.

如果您使用exec,proc_open和其他命令行调用,则以ASCII格式发送所有内容

纯文本不是纯文本,文件有编码

您可以使用iconv过滤器动态转换文件

如果添加了更改功能,我会更新此答案,等等.


是的,对.Mysqli和PDO可以使用他们的本机驱动程序.如果你用`--with-mysqli = mysqlnd --with -pdo-mysql = mysqlnd`选项编译php,他们也可以使用mysqlnd驱动程序.

7> Miguel Steve..:

我最近发现使用strtolower()可能会导致在特殊字符后截断数据的问题.

解决方案是使用

mb_strtolower($string, 'UTF-8');

mb_使用MultiByte.它支持更多字符,但一般来说速度稍慢.



8> 小智..:

我唯一要补充的是这些惊人的答案是强调以utf8编码保存你的文件,我注意到浏览器接受这个属性而不是设置utf8作为你的代码编码.任何体面的文本编辑器都会向您展示这一点,例如Notepad ++有一个用于文件enconding的菜单选项,它会显示当前编码并允许您更改它.对于我所有的php文件,我使用没有BOM的utf8.

前段时间我有人要求我为其他人设计的php/mysql应用程序添加utf8支持,我注意到所有文件都是用ANSI编码的,所以我不得不使用ICONV转换所有文件,更改数据库表使用utf8 charset和utf8_general_ci整理,在连接后将'SET NAMES utf8'添加到数据库抽象层(如果使用5.3.6或更早版本,否则你必须在连接字符串中使用charset = utf8)并更改字符串函数以使用php multibyte字符串函数等效.



9> JW...:

在PHP中,您需要使用多字节函数,或者打开mbstring.func_overload.这样,如果您的字符占用多个字节,strlen就会起作用.

您还需要确定回复的字符集.您可以使用AddDefaultCharset,如上所述,也可以编写返回标头的PHP代码.(或者您可以在HTML文档中添加META标记.)


请注意 - 某些代码实际上可能依赖于标准字符串函数的每字符一字节性质.

10> Abdul Sadik ..:

我刚刚遇到了同样的问题,并在PHP手册中找到了一个很好的解决方案.

我将所有文件编码更改为UTF8,然后将连接的默认编码更改为UTF8.这解决了所有问题.

if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}

查看来源


我花了一个小时试图找出我正在处理的页面上的编码问题,而且我通常很擅长弄清楚东西.我总是查阅这个页面,你的答案对我帮助很大.得到了我的upvote.在我的例子中,`set_charset('utf8mb4')`没有工作但是`> set_charset("utf8")`做了,而其他答案实际上没有显示.

11> jalf..:

PHP中的Unicode支持仍然是一个巨大的混乱.虽然它能够将ISO8859字符串(它在内部使用)转换为utf8,但它缺乏本机处理unicode字符串的能力,这意味着所有字符串处理函数都会破坏和破坏你的字符串.因此,您必须使用单独的库来获得正确的utf8支持,或者自己重写所有字符串处理函数.

简单的部分就是在HTTP头和数据库中指定字符集等,但如果您的PHP代码没有输出有效的UTF8,那么这一切都不重要.这是困难的部分,PHP几乎没有帮助你.(我认为PHP6应该可以解决最糟糕的问题,但那还有一段时间了)



12> 小智..:

如果您希望MySQL服务器决定字符集,而不是PHP作为客户端(旧行为;首选,在我看来),请尝试添加skip-character-set-client-handshake到您的my.cnf,下[mysqld],并重新启动mysql.

如果你使用UTF8以外的任何东西,这可能会造成麻烦.

推荐阅读
无名有名我无名_593
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有