mysqli 没有将字符集设置为 utf8mb4

mysqli not setting charset to utf8mb4

发现问题

问题似乎是 $mysqli->set_charset() 不接受 `utf8mb4' 作为有效编码(就像我在第一次更新中 "speculated" 一样)。 MySQL 版本是 5.5.41,PHP 版本是 5.4.41(没问题)。


对不起这个标题,我已经 searching/reading 关于 what/where 问题是什么,我已经对此感到很困惑...

我最近开始在 mysql 中使用 utf8mb4。我使用 utf8mb4 作为字符集和 utf8mb4_unicode_ci 作为所有 tables/columns.

的排序规则

所以我先改了:

$mysqli->set_charset('utf8');

$mysqli->set_charset('utf8mb4');

确保我的 php 文件是 utf8(我正在使用 Visual Studio 代码,因此文件默认以 UTF-8 创建),并且 php/html headers 设置为 utf8:

index.php

header('Content-type: Text/HTML; Charset=UTF-8');

main.php(包含在index.php末尾)

<meta http-equiv="Content-Type" content="Text/HTML" />
<meta charset="UTF-8" />

问题是对于某些 tables 我必须手动插入数据,并且这些数据按原样存储:带有特殊字符、带有重音符号、ñ 等...当我显示时我网站上的这个数据我可以看到这些字符 已经替换了 special/accented 个字符。

所以我的问题是:有什么方法可以在 mysql 中按原样存储数据(没有 replacing/converting special/accented 个字符)并能够很好地显示它(按原样) ?

如果我恢复到 $mysqli->set_charset('utf8');,数据显示正常...所以这让我想知道按原样存储 utf-8 字符应该没有问题,但某处存在一些编码问题。 ..

我正在使用 sqlyog 社区(与 wine)并且我在某处读到当您更改某些 db/table 配置时 gui 有时无法正常工作,唯一的方法是旧方法(运行 你自己查询),但我还没有试过这个。我 运行 查询设置所有 tables/columns.

的字符集和排序规则

你怎么看?

更新

我开始认为 mysqli 不接受 utf8mb4 作为有效字符编码并使用来自 php 而不是来自 mysql 的 utf8...我也认为mysql fckd 创建 utf8mb4 而不是更新现有的 utf8 以支持 4 个字节....

当我使用 mysqli 字符集 utf8 进行测试时,所有内容都按原样存储并按原样显示(mysql 字符集和排序规则设置为 utf8mb4...)。

更新 2

SELECT name, HEX(name) FROM person LIMIT 1

这是它的输出:

New Person has name Altaïr 416C7461C3AF72

但是正如我已经说过的,这是使用:

$mysqli->set_charset('utf8');

插入并select。如果我改用 utf8mb4,这就是它存储的内容:

Altaïr

但是显示正常。它没有显示的是,如果名称按原样存储,显示的名称将是 Alta�r.

所以问题是:为什么 mysqli/mysql 使用 utf8mb4 将 ï 存储为 ï?为什么在 mysqli 中设置了 utf8mb4 时 php 将 ï 等特殊字符显示为

有人可以确认 mysqli::set_charset 接受 utf8mb4 作为有效编码吗?

更新 3

我有一个 class 函数,它 select 是来自 table "es" 的字符串,例如:Iniciar Sesión(这是存储的)和如果 mysqli 字符集是 utf8,则 selected/displayed 是 Iniciar Sesión.

这可能是一个完全不同的问题,但它显然是另一个编码问题。据我了解,如果 tables/columns 是 utf8mb4 并且 mysqli 设置为 utf8,则 mysql 必须从 utf8(3 字节)编码到 ut8mb4(全字节支持)。所以这意味着 mysqli 不使用来自 php 的 utf8,而是来自 mysql。这是正确的,对吗?

我的应用程序目前在编码方面遇到困难...(但可能是某些服务器配置问题...)

更新 4

问题会出在这里吗?我真的不知道这种配置:

SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';
+--------------------------+--------------------+
| Variable_name            | Value              |
+--------------------------+--------------------+
| character_set_client     | utf8               |
| character_set_connection | utf8               |
| character_set_database   | utf8mb4            |
| character_set_filesystem | binary             |
| character_set_results    | utf8               |
| character_set_server     | latin1             |
| character_set_system     | utf8               |
| collation_connection     | utf8_general_ci    |
| collation_database       | utf8mb4_unicode_ci |
| collation_server         | latin1_swedish_ci  |
+--------------------------+--------------------+
10 rows in set (0.00 sec)

更新 4-1/2(从评论中复制)

CREATE TABLE `es` (
    id int(11) NOT NULL AUTO_INCREMENT, 
    name varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 
    text varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 
    PRIMARY KEY (id), 
    UNIQUE KEY name (name)
) ENGINE=InnoDB AUTO_INCREMENT=76 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci` 

问题可能源于您没有在 MySQL 列定义中使用 utf8mb4(至少您没有说明您使用的是什么编码)。

这是一个 MySQL table 定义的示例,其中一列使用 utfmb4:

CREATE TABLE `person` (
  `name` varchar(255) CHARACTER SET utf8mb4
)

更新

使用以下 table 定义:

CREATE TABLE `person` (
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

和以下 PHP 脚本:

<?php
$mysqli = new mysqli('localhost', 'username', 'password', 'database');
$mysqli->set_charset('utf8mb4');

$mysqli->query("INSERT INTO `person` VALUES ('Altaïr Ibn-La\'Ahad')");

$result = $mysqli->query("SELECT * FROM `person` LIMIT 1");

$person = $result->fetch_object();

if($person)
    printf ("New Person has name %s.\n", $person->name);

$result->close();
$mysqli->close();

当我将 "Altaïr Ibn-La'Ahad" 插入数据库时​​,名称将按原样存储,没有任何变化。该脚本还打印出姓名而没有更改:“New Person has name Altaïr Ibn-La'Ahad.

希望这能帮助您解决问题。让我知道它是否有效。

utf8mb4 Altaïr is 41 6C 74 61 C383C2AF 72

哎呀。即"double encoding"。 latin1 EF 已转换为 utf8/utf8mb4 C3AF;然后 C3 被错误地视为 latin1 被转换为 C383 并且 AF 被转换为 C2AF.

这是可能发生的事情:

  • 客户端的字符编码为utf8(好);和
  • SET NAMES latin1 谎称客户端有 latin1 编码;和
  • 在table列声明CHARACTER SET utf8(或utf8mb4)(好)。

第二步应该已经被

修复了
$mysqli->set_charset('utf8mb4');

我假设您没有混合使用 mysql_*mysqli_* 接口。只使用后者。

发布一个简短的、可重现的测试用例怎么样。

谜底解开了! installation/upgrade/config 和 mysql 有问题,utf8mb4 没有正确安装。

函数的问题在于它使用 utf8_encode() 重新编码 db 值,并且不知何故导致了这些字符 ó -> ó