首页 文章

UTF-8一路走来

提问于
浏览
1076

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

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

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

14 回答

  • 5

    PHP中的Unicode支持仍然是一个巨大的混乱 . 虽然它能够将ISO8859字符串(它在内部使用)转换为utf8,但它缺乏本机处理unicode字符串的能力,这意味着所有字符串处理函数都会破坏和破坏你的字符串 . 因此,您必须使用单独的库来获得正确的utf8支持,或者自己重写所有字符串处理函数 .

    简单的部分就是在HTTP头和数据库中指定字符集等,但如果您的PHP代码没有输出有效的UTF8,那么这一切都不重要 . 这是困难的部分,PHP几乎没有帮助你 . (我认为PHP6应该可以解决最糟糕的问题,但那还有一段时间了)

  • 933

    Data Storage

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

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

    Data Access

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

    • 一些驱动程序提供了自己的配置连接字符集的机制,它们都更新了自己的内部状态,并通知MySQL要在连接上使用的编码 - 这通常是首选方法 . 在PHP中:

    • 如果您使用PHP≥5.3.6的PDO抽象层,则可以在DSN中指定 charset

    $dbh = new PDO('mysql:charset=utf8mb4');
    
    $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 的相同考虑适用于上述情况 .

    Output

    • 如果您的应用程序将文本传输到其他系统,则还需要告知它们字符编码 . 对于Web应用程序,必须通知浏览器发送数据的编码(通过HTTP响应头或HTML metadata) .

    • 在PHP中,您可以使用default_charset php.ini选项,或者自己手动发出 Content-Type MIME标头,这只是更多工作但具有相同的效果 .

    Input

    • 不幸的是,在尝试存储或在任何地方使用它之前,您应该验证每个接收的字符串是否为有效的UTF-8 . PHP的mb_check_encoding()可以解决问题,但你必须虔诚地使用它 . 有's really no way around this, as malicious clients can submit data in whatever encoding they want, and I haven' t发现了一个让PHP可靠地为你做这件事的技巧 .

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

    • 仅适用于HTML5之前的HTML:您希望浏览器发送给您的所有数据都是UTF-8 . 不幸的是,如果您通过唯一的方法可靠地执行此操作,请将 accept-charset 属性添加到所有 <form> 标记: <form ... accept-charset="UTF-8"> .

    • 仅适用于HTML5之前的HTML:请注意,W3C HTML规范表明客户端"should"默认在服务器所服务的任何字符集中将表单发送回服务器,但这显然只是一个建议,因此需要明确每一个 <form> 标签 .

    Other Code Considerations

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

    • 您需要确保每次处理UTF-8字符串时都安全地执行此操作 . 不幸的是,这是困难的部分 . 你'll probably want to make extensive use of PHP' s mbstring扩展名 .

    • PHP's built-in string operations are not by default UTF-8 safe. 使用普通的PHP字符串操作(如连接)可以安全地执行某些操作,但对于大多数情况,您应该使用等效的 mbstring 函数 .

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

  • 13

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

    我将所有文件编码更改为UTF8,然后将连接的默认编码更改为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());
    }
    

    View Source

  • 140

    我想给chazomaticus' excellent answer添加一件事:

    不要忘记META标签(像这样,或the HTML4 or XHTML version of it):

    <meta charset="utf-8">
    

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

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

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

    Edit:

    W3C实际上有一个相当大的section dedicated to I18N . 他们有很多与此问题相关的文章 - 描述HTTP,(X)HTML和CSS方面:

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

  • 12

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

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

    只要你意识到 string functions don't work with Unicode, and some might mangle strings completely 的大部分内容,在PHP中使用Unicode很容易 . PHP认为"characters"长度为1个字节 . 有时这是可以的(例如, explode() 只查找一个字节序列并将其用作分隔符 - 因此,您查找的实际字符无关紧要) . 但有时候,当函数实际设计用于处理字符时,PHP不知道你的文本有多字节字符,可以用Unicode找到 .

    一个很好的图书馆是phputf8 . 这将重写所有"bad"函数,以便您可以安全地处理UTF8字符串 . 像mbstring扩展这样的扩展试图为你做这个,但我更喜欢使用库,因为它对我很重要 . 但是,无论如何,phputf8可以在幕后使用mbstring来提高性能 .

  • 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"));
    

    我从这个网站上下来的网站,幸运地使用谷歌缓存得到它 .

  • 26

    在我的情况下,我使用 mb_split ,它使用正则表达式 . 因此我还必须通过 mb_regex_encoding('UTF-8'); 手动确保正则表达式编码是utf-8

    作为旁注,我还通过运行 mb_internal_encoding() 发现内部编码不是utf-8,我通过运行 mb_internal_encoding("UTF-8"); 来改变它 .

  • 19

    首先,如果你在<5.3PHP,那么没有 . 你有很多问题需要解决 .

    我很惊讶没有人提到intl库,那个对 unicodegraphemesstring operationslocalisation 以及更多内容有很好支持的库,见下文 .

    我将通过 Elizabeth Smith's slidesPHPBenelux'14 引用有关PHP中unicode支持的一些信息

    INTL

    好:

    • ICU图书馆周围的包装

    • 标准化语言环境,为每个脚本设置语言环境

    • 数字格式

    • 货币格式

    • 消息格式化(替换gettext)

    • 日历,日期,时区和时间

    • Transliterator

    • Spoofchecker

    • 资源包

    • 转换器

    • IDN支持

    • Graphemes

    • 整理

    • 迭代器

    坏:

    • 不支持zend_multibite

    • 不支持HTTP输入输出转换

    • 不支持函数重载

    mb_string

    • 启用zend_multibyte支持

    • 支持透明的HTTP输入/输出编码

    • 提供一些funtionallity的包装器,例如strtoupper

    ICONV

    • 主要用于字符集转换

    • 输出缓冲处理程序

    • mime编码功能

    • 转换

    • 一些字符串助手(len,substr,strpos,strrpos)

    • Stream Filter stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

    数据库

    • mysql:表和连接上的字符集和排序规则(不是排序规则) . 也不要使用mysql - msqli或PDO

    • postgresql:pg_set_client_encoding

    • sqlite(3):确保使用unicode和intl支持编译它

    其他一些问题

    • 除非使用第三部分扩展名,否则不能将unicode文件名与PHP和Windows一起使用 .

    • 如果使用exec,proc_open和其他命令行调用,则以ASCII格式发送所有内容

    • 纯文本不是纯文本,文件有编码

    • 您可以使用iconv过滤器动态转换文件

    如果添加了更改功能,我会更新此答案,等等 .

  • 58

    我最近发现使用 strtolower() 会导致问题所在数据在特殊字符后被截断 .

    解决方案是使用

    mb_strtolower($string, 'UTF-8');
    

    mb_使用MultiByte . 它支持更多字符,但一般来说速度稍慢 .

  • 6

    我唯一要补充的是这些惊人的答案是强调以utf8编码保存你的文件,我注意到浏览器接受这个属性而不是设置utf8作为你的代码编码 . 任何体面的文本编辑器都会显示这一点,例如Notepad有一个文件enconding的菜单选项,它会显示当前编码并允许您更改它 . 对于我所有的php文件,我使用没有BOM的utf8 .

    前段时间我有人要求我为其他人设计的php / mysql应用程序添加utf8支持,我注意到所有文件都是用ANSI编码的,所以我不得不使用ICONV转换所有文件,更改数据库表使用utf8 charset和utf8_general_ci整理,在连接后将'SET NAMES utf8'添加到数据库抽象层(如果使用5.3.6或更早版本,否则你必须在连接字符串中使用charset = utf8)并更改字符串函数以使用php multibyte字符串函数等效 .

  • 0

    在PHP中,您需要使用multibyte functions,或打开mbstring.func_overload . 这样,如果您的字符占用多个字节,strlen就会起作用 .

    您还需要确定回复的字符集 . 您可以使用AddDefaultCharset,如上所述,也可以编写返回标头的PHP代码 . (或者您可以在HTML文档中添加META标记 . )

  • 8

    最佳答案非常好 . 这是我在常规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
    

    这就是全部了 !

  • 5

    如果您希望MySQL服务器决定字符集,而不是PHP作为客户端(旧行为;首选,在我看来),请尝试将 skip-character-set-client-handshake 添加到 my.cnf ,在 [mysqld] 下,然后重新启动 mysql .

    如果你使用UTF8以外的任何东西,这可能会造成麻烦 .

  • 20

    如果你想要一个mysql解决方案,我在服务器迁移后遇到了与我的两个项目类似的问题 . 在搜索并尝试了很多解决方案之后,我遇到了这个/之前没有任何解决方案):

    mysqli_set_charset($con,"utf8");
    

    将此行添加到我的配置文件后,一切正常!

    当我想要从html查询解决插入时,我找到了这个解决方案https://www.w3schools.com/PHP/func_mysqli_set_charset.asp

    祝好运!

相关问题