问题:UTF-8贯穿始终

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

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

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

标签:php,mysql,linux,apache,utf-8

回答1:

数据存储

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

  • 在旧版本的MySQL(<5.5.3)中,很不幸,您将被迫仅使用utf8,该代码仅支持Unicode字符的子集。我希望我在开玩笑。

数据访问

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

  • 某些驱动程序提供了自己的配置连接字符集的机制,该机制既可以更新其内部状态,又可以将要在连接上使用的编码通知MySQL-这通常是首选方法。在PHP中:

    • 如果您使用的是 PDO 抽象层在PHP≥5.3.6的情况下,您可以在 DSN

      $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您的应用程序希望如何对连接上的数据进行编码: 设置名称'utf8mb4'

  • 关于utf8mb4 / utf8的相同考虑同样适用。

输出

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

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

  • 使用json_encode()对输出进行编码时,添加JSON_UNESCAPED_UNICODE作为第二个参数。

输入

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

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

    • 仅适用于HTML5之前的HTML :您希望浏览器发送给您的所有数据都采用UTF-8。不幸的是,如果唯一可靠的方法是将accept-charset属性添加到所有
      标签:
    • 仅适用于HTML5之前的HTML :请注意,W3C HTML规范指出,客户端"应该"默认使用服务器提供的任何字符集将表单发送回服务器,但这显然只是建议,因此需要在每个标记上明确显示。

其他代码注意事项

  • 很显然,您要提供的所有文件(PHP,HTML,JavaScript等)都应使用有效的UTF-8编码。

  • 您需要确保每次处理UTF-8字符串时,都必须安全进行。不幸的是,这是困难的部分。您可能需要广泛使用PHP的 mbstring 扩展名。

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

  • 要知道您在做什么(阅读:不要搞砸了),您确实需要了解UTF-8及其在最低级别上的工作方式。查看 utf8.com 中的任何链接,以获取一些好的资源,以学习您需要了解的一切。

回答2:

我想在 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声明)。

回答3:

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

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

在PHP中使用Unicode十分容易,只要您意识到大多数 string函数不适用于Unicode,并且其中某些函数可能会完全破坏字符串。 PHP认为"字符"的长度为1个字节。有时这是可以的(例如,explode()仅查找字节序列并将其用作分隔符-因此,无论要查找什么实际字符都没有关系)。但是有时,当该函数实际上是为使用 characters 设计的时,PHP却不知道您的文本具有以Unicode找到的多字节字符。

phputf8 是一个很好的库。这将重写所有"不良"功能,因此您可以安全地处理UTF8字符串。也有像mbstring扩展这样的扩展也尝试为您完成此操作,但是我更喜欢使用该库,因为它具有更高的可移植性(但是我写的是大众市场产品,所以对我来说很重要)。但是phputf8仍然可以在后台使用mbstring来提高性能。

回答4:

我发现使用PDO的人遇到了问题,答案是将其用于PDO连接字符串:

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

我从中获取该信息的网站已经关闭,但是幸运的是,我能够使用Google缓存来获取它。

回答5:

在我的情况下,我使用的是mb_split,它使用了正则表达式。因此,我还必须通过执行mb_regex_encoding('UTF-8');

来手动确保正则表达式编码为utf-8。

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

回答6:

首先,如果您使用的是<5.3PHP,则不会。您有很多问题要解决。

令我惊讶的是,没有人提到 intl 库,该库对 unicode 字素字符串操作本地化等,请参见下文。

我将通过 Elizabeth Smith的 slides引用有关PHP中的Unicode支持的一些信息。 PHPBenelux'14

INTL

好:

  • ICU库周围的包装器
  • 标准化语言环境,按脚本设置语言环境
  • 数字格式
  • 货币格式
  • 消息格式(替换gettext)
  • 日历,日期,时区和时间
  • 音译器
  • 欺骗检查器
  • 资源包
  • 转换器
  • IDN支持
  • 字素
  • 整理
  • 迭代器

坏:

  • 不支持zend_multibite
  • 不支持HTTP输入输出转换
  • 不支持函数重载

mb_string

  • 启用zend_multibyte支持
  • 支持透明的HTTP输入/输出编码
  • 提供一些包装功能,例如strtoupper

ICONV

  • 主要的字符集转换
  • 输出缓冲区处理程序
  • mime编码功能
  • 转化
  • 一些字符串助手(len,substr,strpos,strrpos)
  • 流过滤器stream_filter_append($fp,'convert.iconv.ISO-2022-JP/EUC-JP')

数据库

  • mysql:表和连接上的字符集和排序规则(不是排序规则)。也不要使用mysql-msqli或PDO
  • PostgreSQL:pg_set_client_encoding
  • sqlite(3):确保使用unicode和intl支持对其进行编译

其他一些陷阱

  • 除非使用第3部分扩展名,否则不能在PHP和Windows中使用Unicode文件名。
  • 如果您使用exec,proc_open和其他命令行调用,则以ASCII格式发送所有内容
  • 纯文本不是纯文本,文件具有编码
  • 您可以使用iconv过滤器动态转换文件

我将更新此答案,以防万一事情改变了添加的功能等等。

回答7:

我要添加到这些惊人答案中的唯一一件事是强调以utf8编码保存文件,我注意到浏览器接受此属性,而不是将utf8设置为您的代码编码。任何体面的文本编辑器都会向您显示此内容,例如Notepad ++具有用于文件编码的菜单选项,它向您显示当前编码并允许您对其进行更改。对于我所有的php文件,我都使用不带BOM的utf8。

有时候,我有人要求我为其他人设计的php / mysql应用程序添加utf8支持,我注意到所有文件均以ANSI编码,因此我不得不使用ICONV来转换所有文件,更改数据库表要使用utf8 charset和utf8_general_ci整理,在连接后将" SET NAMES utf8"添加到数据库抽象层(如果使用5.3.6或更早版本,则必须在连接字符串中使用charset = utf8)并更改要使用的字符串函数php多字节字符串功能等效。

回答8:

我最近发现,使用strtolower()可能会导致数据在特殊字符后被截断的问题。

解决方案是使用

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

mb_使用多字节。它支持更多字符,但总的来说要慢一些。

回答9:

我刚刚经历了同样的问题,并在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());
}

查看源文件

回答10:

在PHP中,您需要使用多字节函数,或打开 mbstring.func_overload 。这样,如果您使用的字符超过一个字节,strlen之类的东西就可以工作。

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

回答11:

PHP对Unicode的支持仍然是一团糟。虽然它能够将ISO8859字符串(在内部使用)转换为utf8,但它本身无法处理unicode字符串,这意味着所有字符串处理功能都会破坏和破坏您的字符串。因此,您必须使用单独的库以获得适当的utf8支持,或者自己重写所有字符串处理函数。

最简单的部分只是在HTTP标头和数据库等中指定字符集,但是如果您的PHP代码没有输出有效的UTF8,那么这都不重要。这是最困难的部分,PHP在那里几乎没有帮助。 (我认为PHP6应该可以解决最坏的情况,但是仍然有一段时间)

回答12:

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

如果您使用的不是UTF8,可能会引起麻烦。

回答13:

最高答案是极好的。这是我在常规的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

仅此而已!

回答14:

如果您想要mysql解决方案,则在服务器迁移后,我的两个项目也遇到了类似的问题。在搜索并尝试了很多解决方案之后,我发现了这一解决方案/在解决此问题之前一无所获):

mysqli_set_charset($con,"utf8");

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

我找到了此解决方案 https://www.w3schools.com/PHP/func_mysqli_set_charset.asp 当我想解决html查询中的插入内容

祝你好运!

回答15:

只是一个注释:

您面临的问题是您的非拉丁字符显示为?????????,您提出了一个问题,并以参考该规范问题而关闭,您尝试了一切,无论您做什么,仍然可以从MySQL获得??????????

主要是因为您正在测试您的旧数据,这些数据已使用错误的字符集插入到数据库中,并已转换并存储为实际的问号字符?。这意味着您将永远失去原始文本,无论您尝试什么,都会得到???????

将您从此问题的答案中学到的知识应用到新的数据上可以解决您的问题。

回到顶部