协慌网

登录 贡献 社区

UTF-8 一路走来

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

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

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

答案

数据存储

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

  • 在旧版本的 MySQL(<5.5.3)中,遗憾的是,您只能使用utf8 ,它只支持 Unicode 字符的子集。我希望我在开玩笑。

数据访问

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

  • 一些驱动程序提供了自己的配置连接字符集的机制,它们都更新了自己的内部状态,并通知 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 标头,这只是更多工作但具有相同的效果。

输入

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

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

    • 对于 HTML5 之前的 HTML :您希望浏览器发送给您的所有数据都是 UTF-8。不幸的是,如果你顺利地做到这一点,那就是将accept-charset属性添加到你的所有<form>标签: <form ... accept-charset="UTF-8">
    • 仅适用于 HTML5 之前的 HTML :请注意,W3C HTML 规范说客户端 “应该” 默认在服务器所服务的任何字符集中将表单发送回服务器,但这显然只是一个建议,因此需要在每个单独显示<form>标签。

其他代码注意事项

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

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

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

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

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

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

<meta charset="utf-8">

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

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

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

编辑:

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

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

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

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

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

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