
您将泰语文本插入MySQL,但查询回来时看到???或乱码字符,如àž àž²àžŠàž²àžšàž±àžš。这是数据库、表、PHP连接和客户端之间的字符集不匹配。这是默认配置的共享主机环境中最常见的问题之一。
本指南解释了根本原因,并提供逐步修复方案——从phpMyAdmin到PHP连接——无需访问服务器级MySQL配置。
快速修复:将字符集设置为utf8mb4,排序规则设置为utf8mb4_unicode_ci,在数据库级别、表级别和PHP连接中都要设置。
1. 为什么泰语字符显示为?
MySQL使用字符集(字符编码)和排序规则(排序规则)将数据存储为二进制字节。当以下情况发生时会出现问题:
- PHP以UTF-8发送数据,但数据库期望Latin1
- 表是用错误的字符集创建的(如
latin1) - PHP连接从未执行
SET NAMES utf8mb4 - 单独列的字符集与表不同
当MySQL无法将传入字节映射到表字符集支持的字符时,它会用?替换它们或存储损坏的数据。
2. Latin1 vs UTF-8 vs utf8mb4
| 字符集 | 泰语支持 | 表情符号 | 推荐 |
|---|---|---|---|
latin1 | 否 | 否 | 避免 |
utf8 (MySQL) | 是 | 否(最多3字节) | 可用但不完整 |
utf8mb4 | 是 | 是(4字节) | 推荐 |
重要:MySQL的utf8并不是标准UTF-8。它每个字符最多只支持3个字节,意味着表情符号和某些字符会被静默损坏。始终使用utf8mb4。
3. 通过phpMyAdmin修复 — ALTER TABLE
通过DirectAdmin打开phpMyAdmin,按照以下步骤操作:
步骤1:更改数据库字符集
点击您的数据库,进入操作标签,找到"排序规则",选择utf8mb4_unicode_ci,然后点击执行。或者直接运行SQL:
ALTER DATABASE `your_database_name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
步骤2:转换所有表
对每个表运行此命令(相应替换表名):
ALTER TABLE `your_table_name` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CONVERT TO关键字更改表中每一列的字符集。这与DEFAULT CHARACTER SET不同,后者只更改新列的默认值。
⚠️ 始终先备份:在运行ALTER TABLE之前,将数据库导出为.sql文件。如果转换过程中数据损坏,您将需要备份来恢复。
4. 修复PHP连接
即使数据库使用utf8mb4,MySQL也不知道您的PHP脚本发送的是什么字符集,除非您明确告知它。
选项1:PDO(推荐)
// PDO connection with charset
$dsn = "mysql:host=localhost;dbname=mydb;charset=utf8mb4";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
]);选项2:mysqli
// mysqli connection
$conn = new mysqli($host, $user, $pass, $dbname);
$conn->set_charset("utf8mb4");5. WordPress的修复方法
WordPress自4.2版本起自动处理此问题。验证您的wp-config.php包含:
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', 'utf8mb4_unicode_ci');注意:如果您迁移了旧的WordPress网站,表可能仍然使用utf8。对每个WordPress表运行ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4,或者如果您的主机支持,使用WP-CLI命令wp db convert-charset。
6. 为什么泰语文本变成??? — 每一层的字符集/排序规则不匹配
理解这个问题最重要的一点是,'泰语文本变成???'从来不是由单一故障点引起的。它是由数据经过的多个层之间的字符集不匹配引起的——从用户输入的HTML表单,一直到磁盘上写入的实际字节。如果任何一层错误地解释了字节,您的泰语字符就会立即损坏,您看到的症状取决于哪一层出错。
想象您的泰语数据按顺序经过四个层。每一层都必须同意使用utf8mb4:
| 层 | 设置位置 | 如果错误会发生什么 |
|---|---|---|
| 1. HTML / 表单 | <meta charset="UTF-8">和Content-Type标头 | 浏览器从一开始就以错误的编码发送字节 |
| 2. PHP连接 | DSN中的SET NAMES utf8mb4 / charset=utf8mb4 | MySQL认为客户端使用latin1并错误地转换字节 |
| 3. 表 / 列 | CREATE/ALTER时的CHARACTER SET utf8mb4 | 存储单元只接受latin1,所以超出范围的字节变成? |
| 4. 数据库 | ALTER DATABASE ... CHARACTER SET utf8mb4 | 新表将永远继承错误的字符集 |
常见症状以及如何判断是哪一层出问题:
- 纯
???(字面问号):列或表是latin1——MySQL收到它无法存储的utf8mb4字节并将其丢弃为?。此数据永久丢失,没有备份无法恢复。 - 乱码字符如
ภาษา(mojibake):字节是完整的,但连接错误地解释了编码——可以通过正确设置SET NAMES或转换数据来修复(见下面的双重编码部分)。 - 泰语显示正常但表情符号变成
?:您使用的是utf8(3字节)而不是utf8mb4(4字节)——表情符号和某些字符需要4字节。
黄金法则:验证所有四层都是utf8mb4。只修复表但忘记连接(或反之)会让问题继续存在,因为数据必须通过每一层而没有任何不匹配。
7. 在每一层正确设置utf8mb4(数据库·表·连接·HTML)
本节收集了您需要在所有层运行的每个命令,按照您实际诊断问题的方式排序。首先检查当前状态,找出哪一层仍然是latin1:
步骤1:检查每一层的当前字符集
在DirectAdmin中打开phpMyAdmin,进入SQL标签,运行这些检查:
-- Database charset/collation SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'your_database_name'; -- Charset of every table in the database SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_database_name'; -- Charset of each column in a table SHOW FULL COLUMNS FROM `your_table_name`; -- Charset the connection is currently using SHOW VARIABLES LIKE 'character_set%'; SHOW VARIABLES LIKE 'collation%';
如果character_set_client、character_set_connection或character_set_results显示latin1,则连接层有问题,必须在您的PHP代码中修复。
步骤2:用SQL在每一层设置字符集
-- Database layer ALTER DATABASE `your_database_name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- Table layer (CONVERT TO changes every column at once) ALTER TABLE `your_table_name` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- Connection layer (run after every connect) SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
对于HTML层,确保显示和提交数据的页面在<head>中声明正确的meta字符集:
<meta charset="UTF-8">
// and the PHP side sends a matching header
header('Content-Type: text/html; charset=utf-8');⚠️ 顺序很重要:在插入新数据之前将连接设置为utf8mb4。如果在连接仍然是latin1时插入,存储的数据从一开始就损坏了——即使表已经是utf8mb4。
8. 将latin1表/列转换为utf8mb4(ALTER、CONVERT)
当您有用latin1创建的遗留表时,转换需要格外小心,因为有两种不同的情况:(1)列数据已经是正确的泰语只是标签错误,或者(2)数据已经乱码,因为以错误的编码存储。
普通情况:使用CONVERT TO转换整个表
如果现有数据显示正确(通过字符集与插入时使用的字符集匹配的连接),使用CONVERT TO,MySQL会正确地将每列的字节重新编码为utf8mb4:
ALTER TABLE `posts` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
一次性转换所有表(生成语句)
如果您有数十个表,运行此查询生成每个表的ALTER语句,然后复制输出并运行它:
SELECT CONCAT( 'ALTER TABLE `', TABLE_NAME, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;' ) AS sql_command FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_database_name' AND TABLE_TYPE = 'BASE TABLE';
特殊情况:通过BLOB逐列转换(防止数据丢失)
如果CONVERT TO损坏了您的数据(因为现有数据已经是仅被标记为latin1的utf8mb4字节),请使用BLOB循环转换,它告诉MySQL"不要触碰字节,只是重新标记字符集":
// Step 1: change the column to BLOB (keep raw bytes) ALTER TABLE `posts` MODIFY `content` BLOB; // Step 2: change back to TEXT with the utf8mb4 label ALTER TABLE `posts` MODIFY `content` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
使用哪种方法:如果现有数据在phpMyAdmin中显示正确(默认使用utf8mb4),使用普通CONVERT TO。但如果数据只有当连接设置为latin1时才显示正确,使用BLOB技术来保留原始字节。
9. 修复已乱码的数据(双重编码)+ WordPress wp-config
双重编码是指utf8mb4数据被解释为latin1然后再次编码为utf8,将单个泰语字符变成一长串乱码字符,如ภาษาไทย而不是'ภาษาไทย'。在这种情况下数据没有丢失,可以恢复。
如何恢复双重编码数据
技巧是告诉MySQL将当前字节解释为latin1(剥去外层编码),然后以utf8mb4读回:
// Preview the result first (does not modify real data) SELECT CONVERT(CAST(CONVERT(`content` USING latin1) AS BINARY) USING utf8mb4) FROM `posts` LIMIT 5; // Once the preview looks right, run the real UPDATE (always back up first!) UPDATE `posts` SET `content` = CONVERT(CAST(CONVERT(`content` USING latin1) AS BINARY) USING utf8mb4);
⚠️ 永远不要运行UPDATE两次:双重编码恢复命令必须恰好运行一次。再次运行会将数据损坏到另一层。先导出备份,然后用SELECT测试,直到确认无误后再运行UPDATE。
在WordPress中设置字符集(wp-config.php)
WordPress自4.2版本起为您设置字符集,但如果您迁移了旧网站,验证wp-config.php包含:
<?php
// WordPress database charset
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', 'utf8mb4_unicode_ci');如果旧WordPress表仍然是utf8或latin1,对每个WordPress表(如wp_posts、wp_postmeta、wp_comments)运行ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4,或者如果您的主机支持,使用WP-CLI:
# Convert the charset of every table via WP-CLI
wp db query "ALTER DATABASE \`$(wp config get DB_NAME)\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"DirectAdmin上的WordPress:如果您没有SSH/WP-CLI访问权限,可以通过phpMyAdmin使用第8节中的生成器查询来转换每个表,然后复制并运行输出——结果是相同的。
10. 修复清单
- 在进行任何更改之前备份数据库。
- 在phpMyAdmin中将数据库字符集设置为
utf8mb4。 - 对每个表运行
ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4。 - 在PDO DSN字符串中添加
charset=utf8mb4。 - 在每次连接后执行
SET NAMES utf8mb4。 - 通过插入泰语文本并选择回来进行测试。
- 如果问题仍然存在,使用
SHOW FULL COLUMNS FROM table_name检查列。
常见问题(FAQ)
为什么泰语数据在phpMyAdmin中看起来正常,但在我的网站上显示为????
因为phpMyAdmin默认将其连接设置为utf8mb4,所以它能正确读取数据。但您的网站PHP代码在连接后可能没有执行SET NAMES utf8mb4,所以MySQL以latin1格式返回数据,导致显示错误。解决方法是在PDO DSN中添加charset=utf8mb4,或每次使用mysqli时调用$conn->set_charset("utf8mb4")。
转换为utf8mb4后,我的旧???数据会恢复吗?
不会。如果数据已经以?存储,原始字节在插入时就已丢失(latin1列无法接受utf8字节)。那些数据永久丢失,只能从备份中恢复。但是,如果症状是mojibake(ภาษา),字节是完整的,可以通过双重编码技术恢复。
我应该使用utf8mb4_unicode_ci还是utf8mb4_general_ci?
使用utf8mb4_unicode_ci。它按照Unicode标准更准确地对多语言字符进行排序和比较,而utf8mb4_general_ci稍快但不够准确。对于混合泰语和其他语言的网站,unicode_ci是更好的选择(MySQL 8还提供了更新的utf8mb4_0900_ai_ci)。
转换字符集之前我真的需要备份吗?
是的——特别是对于CONVERT TO和双重编码UPDATE,它们会修改真实数据。首先通过DirectAdmin中的phpMyAdmin将数据库导出为.sql文件。如果结果不符合预期,您可以进行恢复。
支持完整MySQL和phpMyAdmin的主机服务
AsiaGB主机从500泰铢/年起,配备DirectAdmin、phpMyAdmin、PHP 7.4/8.2/8.3和SSD存储——运行专业网站所需的一切。
查看主机方案