作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Claria旧金山
Verified Expert in Engineering
15 Years of Experience

Francisco是一名专注于跨平台应用程序(Ionic/Cordova)的工程师,专门从事硬件软件技术集成.

Share

As a MySQL or PHP developer, 一旦你走出了英语字符集的舒适范围, 你很快就会发现自己被UTF-8编码的奇妙古怪的世界缠住了.

快速UTF-8入门

Unicode是一种广泛使用的计算行业标准,它定义了唯一数字代码值到当今大多数书面字符集中的字符的全面映射,以帮助实现系统互操作性和数据交换.

UTF-8是一种可变宽度编码,可以表示Unicode字符集中的每个字符. 它的设计是为了向后兼容ASCII,并避免UTF-16和UTF-32中的尾端顺序和字节顺序标记的复杂性. UTF-8已经成为万维网的主要字符编码, 占所有网页的一半以上.

UTF-8使用一到四个字节对每个字符进行编码. Unicode的前128个字符与ASCII一一对应, 使有效的ASCII文本也有效的utf -8编码文本. 正是由于这个原因,限制使用英语字符集的系统可以避免UTF-8带来的复杂性.

For example, 字母A的Unicode十六进制代码是U+0041, 在UTF-8中,它只是用单个字节41进行编码. 作为比较,字符的Unicode十六进制代码
utf8 symbol
U+233B4,在UTF-8中是用四个字节F0 A3 8E B4编码的.

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).

使用MySQL和PHP的UTF-8进行数据编码,使复杂的语言变得简单.

具体来说,我们将在这篇文章中介绍以下内容:

  • 你需要在你的 PHP code and php.ini PHP编码文件
  • 你需要在你的 my.ini file and other mysql相关问题 要知道(包括配置模块需要,如果你使用 Sphinx)
  • How to MySQL database 之前用latin1编码以使用UTF-8编码

PHP UTF-8编码-修改你的PHP.ini file:

你需要做的第一件事是修改你的 php.ini 文件使用UTF-8作为默认字符集:

	Default_charset = "utf-8";

(注意:你可以随后使用 phpinfo() 来验证是否已正确设置.)

很好,现在PHP和UTF-8可以很好地一起工作了. Right?

不完全是这样. 事实上,差远了.

虽然此更改将确保PHP始终输出UTF-8作为字符编码(在浏览器响应内容类型标头中), 如何在PHP中显示UTF-8字符不是那么简单. 您仍然需要对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中将字符串转换为UTF-8时

    有几个PHP函数会失败, 或者至少不像预期的那样, 如果字符表示需要超过1个字节(如UTF-8). 一个例子是 strlen 函数,该函数将返回字节数而不是字符数.

    有两个选项可用于处理此问题:

    • The iconv PHP中默认可用的函数提供了许多这些函数的多字节兼容版本(例如.g., iconv_strlen, etc.). Remember, though, 提供给这些函数的字符串本身必须经过正确编码.

    • 还有 mbstring 扩展到PHP(关于启用和配置它的信息是可用的 here). 这个扩展提供了一组全面的函数,可以正确地考虑多字节编码.

MySQL的UTF-8编码-修改您的my.ini file:

在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%';
    

    输出应该看起来像这样:

          character_set_client | UTF-8                       
          character_set_connection | UTF-8                       
          character_set_database | UTF-8                       
          | character_set_filesystem | binary                    
          character_set_results | UTF-8                       
          character_set_server | UTF-8                       
          character_set_system | UTF-8                       
          /usr/share/mysql/charsets/
    

    如果你看到 latin1 列出其中的任何一个, 再次检查你的配置,确保你已经正确地重新启动了mysql守护进程.

MySQL的UTF-8编码-其他需要考虑的事情:

  • 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 or later, use utf8mb4 而不是UTF-8作为数据库/表/行字符集. 更多信息可用 here.

  • 如果连接的客户端无法指定与MySQL通信的编码, 连接建立后,您可能需要运行以下命令/查询:

      设置名称UTF-8;
    
  • 在确定varchar字段的大小时 数据库建模,不要忘记UTF-8字符每个字符可能需要多达4个字节.

MySQL UTF-8编码-如果你使用Sphinx:

  • 在您的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编码的数据库数据迁移到UTF-8

如果您有一个已经用latin1编码的MySQL数据库, 下面是如何(在MySQL中)转换为UTF-8:

  1. 的配置设置进行了所有修改 my.ini 文件,如上所述.

  2. 执行如下命令:

     ALTER SCHEMA ' your-db-name '默认字符集为UTF-8;
    
  3. 通过命令行,验证所有内容都正确设置为UTF-8

     mysql> show variables like 'char%';
    
  4. 为要转换的表创建一个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
    
  5. 执行全局搜索并将转储文件中的字符集从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文件)!).

  6. From this point, 我们将开始处理数据库数据, 因此,如果还没有备份数据库,那么备份数据库可能是明智的做法. 然后,将转储文件恢复到数据库中:

     mysql> source "DUMP_FILE_TABLE.sql";
    
  7. 搜索任何可能没有正确转换的记录并纠正它们. 因为非ascii字符是设计成多字节的, 我们可以通过比较字节长度和字符长度(i.e.,以识别可能包含需要修复的双编码UTF-8字符的行)。.

    • 查看是否有多字节字符的记录(如果此查询返回零), 那么在你的表中似乎没有任何多字节字符的记录,你可以继续到步骤8).

        mysql> select count(*) from MY_TABLE where LENGTH(MY_FIELD) != CHAR_LENGTH (MY_FIELD);
      
    • 在MySQL中,创建一个用于UTF-8转换的表. 将具有多字节字符的行复制到临时表中:

        创建表temptable
            select * from MY_TABLE where
            LENGTH(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 blob;
        修改temptable表.艺术家名称varchar(128)字符集UTF-8;
      

      (同样,如上所述,请确保为表使用正确的字段类型.)

    • 从临时表中删除只有单字节字符的行:

        delete from MY_TABLE where LENGTH(MY_FIELD) = CHAR_LENGTH (MY_FIELD);
      
    • 将固定行重新插入到原始表中(在执行此操作之前), 您可能希望在该temptable上运行一些选择,以验证它是否得到了正确的更正, 只是为了检查一下).

        替换成MY_TABLE (select * from temptable);
      
  8. 验证剩余的数据和, if necessary, 重复步骤7中的过程(这可能是必要的), for example, 如果数据是三重编码). 进一步的错误(如果有的话)可能最容易手动解决.

源代码和资源文件

另一件要记住和验证的事情是您的源代码文件, resources files, and so on, 都被妥善保存与UTF-8数据编码. 否则,这些文件中的任何“特殊”字符都可能无法正确处理.

In Netbeans, for example, 您可以右键单击您的项目, 选择属性,然后在“来源”中,您将找到数据编码选项(通常默认为UTF-8), 但这是值得检查的).

或者在Windows记事本中, 使用文件菜单中的“另存为…”选项, 并选择对话框底部的UTF-8编码选项. (注意,记事本提供的“Unicode”选项实际上是UTF-16, 所以这不是你想要的.)

Wrap-up

虽然它可能有点乏味, 花时间通过这些步骤来系统地解决MySQL和PHP的UTF-8数据编码问题,最终可以为您节省大量的时间和痛苦. In the long run, 这种有条理的方法远远优于不断修补系统的普遍趋势.

本指南希望强调在首先设置项目环境时考虑字符集定义的重要性,并在软件项目环境中工作,在其对文本和字符串的操作中适当地考虑字符编码.

了解基本知识

  • 什么是UTF-8字符集?

    由Unicode标准定义, UTF-8是一种能够存储任何Unicode字符的8位字符编码. 它与ASCII向后兼容.

  • UTF-8代表什么?

    UTF是Unicode转换格式的缩写, 而“8”后缀表示使用8位块来表示字符.

  • 如何使用PHP在MySQL中插入Unicode字符?

    为了在MySQL中插入Unicode字符, 您需要创建一个支持Unicode的表, 选择适当的编码/排序设置, 并指定MySQL连接中的字符集. 然后,您可以继续使用PHP代码来插入Unicode.

聘请Toptal这方面的专家.
Hire Now
Claria旧金山

Claria旧金山

Verified Expert in Engineering
15 Years of Experience

Córdoba,阿根廷科尔多瓦

2012年11月19日成为会员

作者简介

Francisco是一名专注于跨平台应用程序(Ionic/Cordova)的工程师,专门从事硬件软件技术集成.

作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 privacy policy.

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 privacy policy.

Toptal开发者

Join the Toptal® community.