作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
As a MySQL or PHP developer, 一旦你走出了英语字符集的舒适范围, 你很快就会发现自己被UTF-8编码的奇妙古怪的世界缠住了.
On a previous job在显示来自世界各地的艺术家的BIOS时,我们开始遇到数据编码问题. 很快就发现存储的数据有问题, 因为有时数据是正确编码的,有时不是.
这导致程序员实现了大杂烩式的补丁, 有时用JavaScript, 有时使用HTML字符集元标签, 有时用PHP, and so on. Soon, 最后我们列出了600个,000艺术家BIOS与双或三编码信息, 数据以不同的方式存储,这取决于谁编写了功能或实现了补丁. 一个经典的技术老鼠窝.
Indeed, 浏览UTF-8数据编码问题可能是一种令人沮丧和紧张的体验. 这篇文章提供了一个简明的食谱,特别是在使用PHP和MySQL时解决这些UTF-8问题, 基于实际经验和教训(并感谢), in part, 发现的信息 here and here along the way).
具体来说,我们将在这篇文章中介绍以下内容:
php.ini
file and PHP code.my.ini
文件和其他 mysql相关问题 要知道(包括配置模块需要,如果你使用 Sphinx)你需要做的第一件事是修改你的 php.ini
文件使用UTF-8作为默认字符集:
Default_charset = "utf-8";
(注意:你可以随后使用 phpinfo()
来验证是否已正确设置.)
很好,现在PHP和UTF-8可以很好地一起工作了. Right?
不完全是这样. 事实上,差远了.
虽然此更改将确保PHP始终输出UTF-8作为字符编码(在浏览器响应内容类型标头中), 您仍然需要对PHP代码进行一些修改,以确保它能够正确地处理和生成UTF-8字符.
要确保PHP代码在UTF-8数据编码沙盒中运行良好, 以下是你需要做的事情:
将UTF-8设置为PHP代码输出的所有标头的字符集
在每个PHP输出报头中,指定UTF-8作为编码:
header('Content-Type: text/html; charset=utf-8');
指定UTF-8作为XML的编码类型
从XML中去掉不支持的字符
因为不是所有的UTF-8字符在XML文档中都被接受, 您需要从生成的任何XML中去掉这些字符. 这是一个很有用的函数(我发现) here)为:
函数utf8_for_xml(字符串)美元
{
返回preg_replace (' / ^ \ [x x {000} {0009} \ \ {000 d} \ x {0020} - {D7FF} \ \ x x x {FFFD} {E000} - \] + / u ',
' ', $string);
}
下面是你如何在代码中使用这个函数:
$safeString = utf8_for_xml($yourUnsafeString);
指定UTF-8作为所有HTML内容的字符集
对于HTML内容,指定UTF-8作为编码:
在HTML表单中,指定UTF-8作为编码:
在所有调用中指定UTF-8作为编码 htmlspecialchars函数
e.g.:
htmlspecialchars函数($str, ENT_NOQUOTES, "UTF-8")
*Note: As of PHP 5.6.0, default_charset
值作为默认值. From PHP 5.4.0, UTF-8是默认值,但在PHP 5之前.4.0, ISO-8859-1作为默认值. 因此,始终显式地指定UTF-8是安全的,这是一个好主意, 尽管这个参数在技术上是可选的.
还要注意,对于UTF-8, htmlspecialchars函数
and htmlentities
可以互换使用吗.
设置UTF-8作为所有MySQL连接的默认字符集
指定UTF-8作为与MySQL数据库交换数据时使用的默认字符集 mysql_set_charset
:
$link = mysql_connect('localhost', 'user', 'password');
mysql_set_charset (use utf8,美元的链接);
注意,从PHP 5开始.5.0, mysql_set_charset
已弃用,并且 mysqli: set_charset
应该改为:
$mysqli = new mysqli("localhost", "my_user", "my_password", "test");
/*检查连接*/
If (mysqli_connect_errno()) {
printf("连接失败:%s\n", mysqli_connect_error());
exit();
}
/*更改字符集为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());
}
$mysqli->close();
始终使用UTF-8兼容版本的字符串操作函数
有几个PHP函数会失败, 或者至少不像预期的那样, 如果字符表示需要超过1个字节(如UTF-8). 一个例子是 strlen
函数,该函数将返回字节数而不是字符数.
有两个选项可用于处理此问题:
在MySQL/UTF-8方面,对 my.ini
所需文件如下:
在每个标签后设置如下配置参数:
[client]
default-character-set = utf - 8
[mysql]
default-character-set = utf - 8
[mysqld]
Character-set-client-handshake = false #强制编码为uft8
character-set-server = utf - 8
collation-server = UTF-8_general_ci
[mysqld_safe]
default-character-set = utf - 8
在对您的 my.ini
文件,重新启动MySQL守护进程.
为了验证一切都已正确设置为使用UTF-8编码, 执行如下查询:
mysql> show variables like 'char%';
输出应该看起来像这样:
| UTF-8
| UTF-8
| UTF-8
|二进制文件
| UTF-8
| UTF-8
| UTF-8
| /usr/share/mysql/charsets/
如果你看到 latin1
列出其中的任何一个, 再次检查你的配置,确保你已经正确地重新启动了mysql守护进程.
MySQL UTF-8实际上是完整UTF-8字符集的部分实现. Specifically, MySQL的UTF-8编码最多使用3个字节, 而编码完整的UTF-8字符集需要4个字节. 这适用于所有语言字符, 但如果你需要支持星体符号(其代码点范围从U+010000到U+10FFFF), 这些需要四字节编码,而MySQL的UTF-8不支持. In MySQL 5.5.的支持解决了这个问题 utf8mb4 character set 它每个字符最多使用四个字节,从而支持完整的UTF-8字符集. 所以如果你使用MySQL 5.5.3、以后使用 utf8mb4
而不是UTF-8作为数据库/表/行字符集. 更多信息可用 here.
如果连接的客户端无法指定与MySQL通信的编码, 连接建立后,您可能需要运行以下命令/查询:
设置名称UTF-8;
在确定varchar字段的大小时 数据库建模,不要忘记UTF-8字符每个字符可能需要多达4个字节.
在您的Sphinx配置文件(i.e., sphinx.conf
):
设置你的索引定义如下:
Charset_type = utf-8
将以下内容添加到源定义中:
sql_query_pre = SET CHARACTER_SET_RESULTS=UTF-8
sql_query_pre = SET NAMES UTF-8
重新启动引擎并重制所有索引.
如果您想配置sphinx,以便C C Ć Č Ĉ ł Ċ * Č š等字母在搜索时都被视为等效的, 您将需要配置一个 charset_table
(a.k.a. 字符折叠),本质上是字符之间的等价映射. 更多信息请访问 here.
如果您有一个已经用latin1编码的MySQL数据库, 下面是如何将latin1转换为UTF-8:
的配置设置进行了所有修改 my.ini
文件,如上所述.
执行如下命令:
ALTER SCHEMA ' your-db-name '默认字符集为UTF-8;
通过命令行,验证所有内容都正确设置为UTF-8
mysql> show variables like 'char%';
为要转换的表创建一个latin1编码的转储文件:
mysqldump -u USERNAME -pDB_PASSWORD——opt——skip-set-charset——default-character-set=latin1
--skip-extended-insert DATABASENAME --tables TABLENAME >
DUMP_FILE_TABLE.sql
e.g:
Mysqldump -u root——opt——skip-set-charset——default-character-set=latin1
--skip-extended-insert artists-database --tables tbl_artist >
tbl_artist.sql
执行全局搜索并将转储文件中的字符集从latin1替换为UTF-8:
e.g., using Perl:
perl -i -pe 's/DEFAULT CHARSET=latin1/DEFAULT CHARSET=UTF-8/' DUMP_FILE_TABLE . perl.sql
Windows用户注意事项: 这种字符集字符串替换(从latin1到UTF-8)也可以使用WordPad(或其他一些文本编辑器)中的查找和替换来完成, such as vim). 请确保按原样保存文件(不要将其保存为unicode txt文件)!).
从这一点开始, 我们将开始处理数据库数据, 因此,如果还没有备份数据库,那么备份数据库可能是明智的做法. 然后,将转储文件恢复到数据库中:
mysql> source "DUMP_FILE_TABLE.sql";
搜索任何可能没有正确转换的记录并纠正它们. 因为非ascii字符是设计成多字节的, 我们可以通过比较字节长度和字符长度(i.e.,以识别可能包含需要修复的双编码UTF-8字符的行)。.
查看是否有多字节字符的记录(如果此查询返回零), 那么在你的表中似乎没有任何多字节字符的记录,你可以继续到步骤8).
mysql> select count(*) from MY_TABLE where 长度(MY_FIELD) != CHAR_LENGTH (MY_FIELD);
将具有多字节字符的行复制到临时表中:
创建表temptable
select * from MY_TABLE where
长度(MY_FIELD) != CHAR_LENGTH (MY_FIELD));
将双编码的UTF-8字符转换为正确的UTF-8字符
这实际上有点棘手. 双编码字符串是被正确编码为UTF-8的字符串. 然而,MySQL错误地帮了我们的忙,把它从它的 thought 从拉丁到UTF-8 again,当我们将列设置为UTF-8编码时. 因此,解决这个问题需要两个步骤,我们通过“欺骗”MySQL来阻止它帮我们这个“忙”。.
首先,将列的编码类型设置回latin1,从而删除双编码:
e.g.:
修改temptable表.ArtistName varchar(128)字符集latin1;
注意:请确保为表使用正确的字段类型. 在上面的例子中, for our table, “ArtistName”的正确字段类型是varchar(128), 但表格中的字段可以是文本或任何其他类型. 一定要正确地指定它!
现在的问题是, 如果我们将列编码设置回UTF-8, MySQL将再次为我们运行latin1到UTF-8的数据编码,我们将回到我们开始的地方. 为了避免这种情况,我们将列类型更改为blob,然后将其设置为UTF-8. 这利用了MySQL不会尝试对blob进行编码的事实. 因此,我们能够“愚弄”MySQL字符集转换,以避免双重编码问题.
e.g.:
修改temptable表.ArtistName团;
修改temptable表.艺术家名称varchar(128)字符集UTF-8;
(同样,如上所述,请确保为表使用正确的字段类型.)
从临时表中删除只有单字节字符的行:
delete from MY_TABLE where 长度(MY_FIELD) = CHAR_LENGTH (MY_FIELD);
将固定行重新插入到原始表中(在执行此操作之前), 您可能希望在该temptable上运行一些选择,以验证它是否得到了正确的更正, 只是为了检查一下).
替换成MY_TABLE (select * from temptable);
验证剩余的数据和, if necessary, 重复步骤7中的过程(这可能是必要的), for example, 如果数据是三重编码). 进一步的错误(如果有的话)可能最容易手动解决.
另一件要记住和验证的事情是您的源代码文件, 资源文件, and so on, 都被妥善保存与UTF-8数据编码. 否则,这些文件中的任何“特殊”字符都可能无法正确处理.
In Netbeans, for example, 您可以右键单击您的项目, 选择属性,然后在“来源”中,您将找到数据编码选项(通常默认为UTF-8), 但这是值得检查的).
或者在Windows记事本中, 使用文件菜单中的“另存为…”选项, 并选择对话框底部的UTF-8编码选项. (注意,记事本提供的“Unicode”选项实际上是UTF-16, 所以这不是你想要的.)
虽然它可能有点乏味, 花时间通过这些步骤来系统地解决MySQL和PHP的UTF-8数据编码问题,最终可以为您节省大量的时间和痛苦. 从长远来看, 这种有条理的方法远远优于不断修补系统的普遍趋势.
本指南希望强调在首先设置项目环境时考虑字符集定义的重要性,并在软件项目环境中工作,在其对文本和字符串的操作中适当地考虑字符编码.
由Unicode标准定义, UTF-8是一种能够存储任何Unicode字符的8位字符编码. 它与ASCII向后兼容.
UTF是Unicode转换格式的缩写, 而“8”后缀表示使用8位块来表示字符.
为了在MySQL中插入Unicode字符, 您需要创建一个支持Unicode的表, 选择适当的编码/排序设置, 并指定MySQL连接中的字符集. 然后,您可以继续使用PHP代码来插入Unicode.
Francisco是一名专注于跨平台应用程序(Ionic/Cordova)的工程师,专门从事硬件软件技术集成.
15
世界级的文章,每周发一次.
世界级的文章,每周发一次.