我正在设置一个新的服务器,并希望在我的web应用程序中完全支持UTF-8。我过去曾在现有的服务器上尝试过这种方法,但似乎总是不得不回到ISO-8859-1。

我到底需要在哪里设置编码/字符集?我知道我需要配置Apache、MySQL和PHP来做到这一点-是否有一些标准的检查表,我可以遵循,或者排除哪里发生了不匹配?

这是一个新的Linux服务器,运行MySQL 5, PHP, 5和Apache 2。


当前回答

对于chazomaticus的精彩回答,我想补充一点:

也不要忘记META标签(像这样,或者它的HTML4或XHTML版本):

<meta charset="utf-8">

这看起来微不足道,但IE7以前就给过我这样的问题。

我做的每件事都是对的;数据库、数据库连接和内容类型HTTP头都被设置为UTF-8,在所有其他浏览器中都能正常工作,但ie仍然坚持使用“西欧”编码。

结果发现这个页面缺少META标签。加上这个,问题就解决了。

编辑:

W3C实际上有相当大的一部分专门讨论I18N。他们有很多关于这个问题的文章——描述HTTP, (X)HTML和CSS方面的事情:

常见问题:更改(X)HTML页面编码为UTF-8 在HTML中声明字符编码 教程:XHTML, HTML和CSS中的字符集和编码 设置HTTP字符集参数

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

其他回答

警告:此答案适用于PHP 5.3.5及以下版本。不要在PHP 5.3.6版本(2011年3月发布)或更高版本中使用它。 比较Palec的回答PDO + MySQL和破碎的UTF-8编码。


我发现了一个问题,有人使用PDO,答案是使用这个PDO连接字符串:

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

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

我把所有文件的编码改为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());
}

查看源代码

PHP中的Unicode支持仍然是一个巨大的混乱。虽然它能够将ISO 8859字符串(它在内部使用)转换为UTF-8,但它缺乏原生处理Unicode字符串的能力,这意味着所有的字符串处理函数都会破坏和破坏您的字符串。

因此,您必须使用单独的库来获得适当的UTF-8支持,或者自己重写所有字符串处理函数。

简单的部分是在HTTP头文件和数据库中指定字符集,但如果PHP代码没有输出有效的UTF-8,这些都无关紧要。这是最困难的部分,PHP在这方面几乎没有提供任何帮助。(我认为PHP 6应该能解决最糟糕的问题,但这还需要一段时间。)

我唯一想要补充的是,强调以UTF-8编码保存文件,我注意到浏览器接受这个属性,而不是设置UTF-8作为代码编码。任何像样的文本编辑器都会显示这一点。例如,notepad++有一个用于文件编码的菜单选项,它会显示当前的编码并允许您更改它。对于我所有的PHP文件,我使用UTF-8没有BOM。

前一段时间,有人让我为别人设计的PHP和MySQL应用程序添加UTF-8支持。我注意到所有的文件都是用ANSI编码的,所以我必须使用iconv来转换所有的文件,将数据库表更改为使用UTF-8字符集和utf8_general_ci排序,在连接后将' set NAMES utf8'添加到数据库抽象层(如果使用5.3.6或更早的版本)。否则,您必须在连接字符串中使用charset=utf8)并更改字符串函数以使用等价的PHP多字节字符串函数。

数据存储:

在数据库中的所有表和文本列上指定utf8mb4字符集。这使得MySQL在物理上存储和检索原生UTF-8编码的值。注意,如果指定了utf8mb4_*排序规则(没有任何显式字符集),MySQL将隐式使用utf8mb4编码。 在MySQL的旧版本(< 5.5.3)中,不幸的是,您只能使用utf8,它只支持Unicode字符的一个子集。我希望我是在开玩笑。

数据访问:

在您的应用程序代码(例如PHP)中,无论您使用何种数据库访问方法,都需要将连接字符集设置为utf8mb4。通过这种方式,MySQL在将数据传递给应用程序时不会从其原生UTF-8进行转换,反之亦然。 一些驱动程序提供了自己的机制来配置连接字符集,它既更新自己的内部状态,又通知MySQL要在连接上使用的编码——这通常是首选的方法。在PHP中: 如果你使用PHP≥5.3.6的PDO抽象层,你可以在DSN中指定字符集: $dbh = new PDO('mysql:charset=utf8mb4'); 如果你正在使用mysqli,你可以调用set_charset(): mysqli - > set_charset(“utf8mb4”);//面向对象的样式 mysqli_set_charset(链接,美元“utf8mb4”);//程序风格 如果你只能使用普通的mysql,但碰巧运行的PHP≥5.2.3,你可以调用mysql_set_charset。 如果驱动程序没有提供自己的机制来设置连接字符集,你可能必须发出一个查询来告诉MySQL你的应用程序期望连接上的数据如何编码:set NAMES 'utf8mb4'。 对于utf8mb4/utf8,同样的考虑也适用于上面。

输出:

在HTTP报头中应该设置UTF-8,例如Content-Type: text/html;charset = utf - 8。你可以通过在php.ini中设置default_charset来实现(首选),或者手动使用header()函数。 如果您的应用程序将文本传输到其他系统,它们也需要被告知字符编码。对于web应用程序,必须通知浏览器发送数据的编码(通过HTTP响应报头或HTML元数据)。 当使用json_encode()对输出进行编码时,添加JSON_UNESCAPED_UNICODE作为第二个参数。

输入:

浏览器将以文档指定的字符集提交数据,因此不需要对输入进行任何操作。 如果您对请求编码有疑问(以防它可能被篡改),您可以在尝试存储它或在任何地方使用它之前验证每个接收到的字符串是否为有效的UTF-8。PHP的mb_check_encoding()可以做到这一点,但您必须认真使用它。实际上没有办法绕过这个问题,因为恶意客户端可以以它们想要的任何编码提交数据,而且我还没有找到让PHP可靠地为您做这件事的技巧。

其他代码注意事项:

显然,您将提供的所有文件(PHP、HTML、JavaScript等)都应该使用有效的UTF-8编码。 您需要确保每次处理UTF-8字符串时都是安全的。不幸的是,这是最难的部分。您可能希望大量使用PHP的mbstring扩展。 PHP的内置字符串操作在默认情况下不是UTF-8安全的。使用普通的PHP字符串操作(如连接)可以安全地完成一些事情,但对于大多数事情,您应该使用等效的mbstring函数。 要知道你在做什么(不要搞砸),你真的需要知道UTF-8以及它是如何在尽可能低的级别上工作的。查看utf8.com上的任何链接,获取一些好的资源,学习你需要知道的一切。