UTF-8一路通过
我正在build立一个新的服务器,并希望在我的web应用程序中完全支持UTF-8。 过去我在现有的服务器上尝试过,似乎总是不得不回退到ISO-8859-1。
我到底需要设置编码/字符集? 我知道我需要configurationApache,MySQL和PHP来做到这一点 – 是否有我可以遵循的标准清单,或者可能排除发生不匹配的问题?
这是一个新的Linux服务器,运行MySQL 5,PHP 5和Apache 2。
数据存储 :
-
在数据库中的所有表和文本列上指定
utf8mb4
字符集。 这使MySQL实际存储和检索UTF-8本地编码的值。 请注意,如果指定了utf8mb4_*
sorting规则(没有任何明确的字符集),MySQL将隐式使用utf8mb4
编码。 -
在旧版本的MySQL(<5.5.3)中,不幸的是你不得不简单地使用
utf8
,它只支持Unicode字符的一个子集。 我希望我在开玩笑。
数据访问 :
-
在您的应用程序代码(例如PHP)中,无论您使用
utf8mb4
数据库访问方法,都需要将连接字符集设置为utf8mb4
。 这样,MySQL在将数据传递到应用程序时不会从其本机UTF-8进行转换,反之亦然。 -
一些驱动程序提供了自己的configuration连接字符集的机制,它们都会更新自己的内部状态,并通知MySQL在连接上使用的编码 – 通常这是首选方法。 在PHP中:
-
如果您使用PHP≥5.3.6的PDO抽象层,则可以在DSN中指定
charset
:$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_charset
php.ini选项,或手动发布Content-Type
MIME头,这只是更多的工作,但效果相同。
input :
-
不幸的是,在尝试存储或在任何地方使用它之前,您应该validation每个收到的string是否为有效的UTF-8。 PHP的
mb_check_encoding()
有诀窍,但是你必须虔诚地使用它。 真的没有办法解决这个问题,因为恶意的客户端可以用任何他们想要的编码提交数据,而且我还没有find一个让PHP可靠地为你做这件事的技巧。 -
从我阅读当前的HTML规范来看,下面的子项目对现代HTML来说不是必须的,甚至是无效的。 我的理解是,浏览器将使用为文档指定的字符集提交数据。 但是,如果您的目标是老版本的HTML(XHTML,HTML4等),这些点可能仍然有用:
- 对于仅HTML5之前的HTML :您希望浏览器发送给您的所有数据都是UTF-8。 不幸的是,如果你唯一可以做到的方法是将
accept-charset
属性添加到所有的<form>
标签中:<form ... accept-charset="UTF-8">
。 - 对于仅HTML5之前的HTML :请注意,W3C HTML规范指出,客户端“应该”默认将表单发送回服务器,无论服务器使用何种字符集,但这显然只是一个build议,因此需要在每一个单独的
<form>
标签。
- 对于仅HTML5之前的HTML :您希望浏览器发送给您的所有数据都是UTF-8。 不幸的是,如果你唯一可以做到的方法是将
其他代码注意事项 :
-
显然,所有你要服务的文件(PHP,HTML,JavaScript等)都应该用有效的UTF-8编码。
-
您需要确保每次处理UTF-8string时都安全。 不幸的是,这是困难的部分。 你可能会想大量使用PHP的
mbstring
扩展。 -
PHP的内置string操作在默认情况下不是 UTF-8安全的。 有一些事情可以用普通的PHPstring操作(比如串联)来安全地完成,但是对于大多数情况,您应该使用等效的
mbstring
函数。 -
要知道你在做什么(阅读:不要搞砸),你真的需要知道UTF-8以及它如何在尽可能低的水平上工作。 查看任何来自utf8.com的链接,了解一切您需要了解的优质资源。
我想补充一点, chazomaticus的优秀答案 :
不要忘记META标签(像这样,或者它的HTML4或XHTML版本 ):
<meta charset="utf-8">
这似乎微不足道,但IE7给了我以前的问题。
我一切正常。 数据库,数据库连接和Content-Type HTTP头都设置为UTF-8,并且在所有其他浏览器中都能正常工作,但Internet Explorer仍然坚持使用“西欧”编码。
原来,页面缺lessMETA标签。 添加解决了这个问题。
编辑:
W3C实际上有一个相当大的部分致力于I18N 。 他们有许多与这个问题有关的文章 – 描述HTTP,(X)HTML和CSS方面的东西:
- FAQ:将(X)HTML页面编码更改为UTF-8
- 在HTML中声明字符编码
- 教程:XHTML,HTML和CSS中的字符集和编码
- 设置HTTP字符集参数
他们build议同时使用HTTP标头和HTML元标记(或者在XHTML作为XML的情况下使用XML声明)。
除了在php.ini中设置default_charset
之外,还可以在任何输出之前使用代码中的header()
发送正确的字符集:
header('Content-Type: text/html; charset=utf-8');
在PHP中使用Unicode很容易,只要您意识到大部分string函数都不适用于Unicode,而且有些可能会彻底破坏string 。 PHP认为“字符”是1个字节长。 有时候这是可以的(例如, explode()
只查找一个字节序列并将其用作分隔符 – 因此,查找的实际字符无关紧要)。 但是有些时候,如果函数实际上是为了处理字符而devise的,那么PHP不知道您的文本是否具有Unicode字符。
一个好的图书馆是phputf8 。 这将重写所有“坏”function,以便您可以安全地使用UTF8string。 有像mbstring扩展那样的扩展,也是为了你这样做,但是我更喜欢使用这个库,因为它更便携(但我写了大众市场的产品,所以这对我很重要)。 但是无论如何,phputf8可以在后台使用mbstring来提高性能。
老话题,我知道。 发现有人使用PDO的问题,答案是使用这个PDO连接string:
$pdo = new PDO( 'mysql:host=mysql.example.com;dbname=example_db', "username", "password", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
我从这个网站拿下来,幸运的是能够使用谷歌caching得到它。
在我的情况下,我使用mb_split
,它使用正则expression式。 因此,我还必须通过执行mb_regex_encoding('UTF-8');
来手动确保正则expression式编码是utf-8 mb_regex_encoding('UTF-8');
另外,我还通过运行mb_internal_encoding()
发现内部编码不是utf-8,并通过运行mb_internal_encoding("UTF-8");
改变它mb_internal_encoding("UTF-8");
。
首先如果你在<5.3PHP那么没有。 你有很多问题需要解决。
我很惊讶没有提到国际图书馆,这个图书馆对unicode , 字形 , string操作 , 本地化等有很好的支持,见下文。
我将在PHPBenelux'14上引用 伊丽莎白史密斯的 幻灯片中有关PHP的unicode支持的一些信息
INTL
好:
- ICU图书馆周围的包装
- 标准化的区域设置,每个脚本设置区域设置
- 数字格式
- 货币格式
- 消息格式(replacegettext)
- 日历,date,时区和时间
- Transliterator
- Spoofchecker
- 资源包
- 转换器
- IDN支持
- 字形
- 整理
- 迭代器
坏:
- 不支持zend_multibite
- 不支持HTTPinput输出转换
- 不支持函数重载
mb_string
- 启用zend_multibyte支持
- 支持透明的HTTPinput/输出编码
- 提供了一些function,如strtoupper包装
ICONV
- 主要用于字符集转换
- 输出缓冲区处理器
- MIME编码function
- 转变
- 一些string助手(len,substr,strpos,strrpos)
- Stream Filter
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支持编译的
其他一些问题
- 除非使用第三部分扩展名,否则不能在PHP和Windows中使用unicode文件名。
- 如果使用exec,proc_open和其他命令行调用,则以ASCII格式发送所有内容
- 纯文本不是纯文本,文件有编码
- 您可以使用iconvfilter即时转换文件
我会更新这个答案的情况下,改变function添加等。
我将添加到这些惊人的答案的唯一的事情是强调保存您的文件在utf8编码,我已经注意到,浏览器通过设置utf8作为您的代码编码接受此属性。 任何像样的文本编辑器都会告诉你这个,例如Notepad ++有一个菜单选项来显示文件,它显示你当前的编码,并且允许你改变它。 对于我所有的php文件,我使用utf8没有BOM。
前段时间我有人要求我为其他人devise的php / mysql应用程序添加utf8支持,我注意到所有文件都是用ANSI编码的,所以我必须使用ICONV来转换所有文件,更改数据库表以使用utf8 charset和utf8_general_ci collate,在连接之后向数据库抽象层添加'SET NAMES utf8'(如果使用5.3.6或更早的版本,否则必须在连接string中使用charset = utf8)并更改string函数以使用php多字节string函数等效。
我最近发现使用strtolower()
可能会导致数据在特殊字符后被截断的问题。
解决scheme是使用
mb_strtolower($string, 'UTF-8');
mb_使用MultiByte。 它支持更多的字符,但一般来说会慢一点。
在PHP中,您需要使用多字节函数 ,或者打开mbstring.func_overload 。 这样的话,像strlen这样的东西就可以工作,如果你有多个字节的字符。
您还需要识别您的回复的字符集。 您可以像上面那样使用AddDefaultCharset,也可以编写返回标题的PHP代码。 (或者你可以添加一个META标签到你的HTML文档。)
从一开始就有好的目标 – 基于你的网站的性质,我发现很多关于谷歌search的资源 – 当然,你不是第一个处理它的人。
神秘的PHP6应该已经把这一切理顺了,对吧?
你几乎可以在服务器级别将utf-8设置为mysql的全局默认字符集,并且将默认正确地默认为更精细的级别。
我刚刚经历了同样的问题,并在PHP手册中find了一个很好的解决scheme。
我改变了我的所有文件编码为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()); }
查看来源
最好的答案是非常好的。 这是我必须在一个正常的Debian / PHP / MySQL的设置:
// storage // debian. apparently already utf-8 // retrieval // the mysql database was stored in utf-8, // but apparently php was requesting iso. this worked: // ***notice "utf8", without dash, this is a mysql encoding*** mysql_set_charset('utf8'); // delivery // php.ini did not have a default charset, // (it was commented out, shared host) and // no http encoding was specified in the apache headers. // this made apache send out a utf-8 header // (and perhaps made php actually send out utf-8) // ***notice "utf-8", with dash, this is a php encoding*** ini_set('default_charset','utf-8'); // submission // this worked in all major browsers once apache // was sending out the utf-8 header. i didnt add // the accept-charset attribute. // processing // changed a few commands in php, like substr, // to mb_substr
就这些 !
在PHP中的Unicode支持仍然是一个巨大的混乱。 虽然它能够将ISO8859string(在内部使用)转换为utf8,但是它本身没有使用unicodestring的能力,这意味着所有的string处理函数都会破坏和破坏string。 所以你必须使用一个单独的库来获得适当的utf8支持,或者自己重写所有的string处理函数。
简单的部分只是在HTTP头文件和数据库中指定字符集等,但如果您的PHP代码不输出有效的UTF8,那么这些都不重要。 这是困难的部分,PHP给你几乎没有帮助。 (我认为PHP6应该可以解决这个问题,但是这还有一段时间了)
如果你希望MySQL服务器决定字符集,而不是PHP作为客户端(老行为;在我看来,首选),尝试添加skip-character-set-client-handshake
到你的my.cnf
,在[mysqld]
,重启mysql
。
如果您使用的不是UTF8,可能会造成麻烦。