utf8mb4(支援 Emoji)<meta charset="utf-8"> + HTTP Header Content-Type: text/html; charset=utf-8SET NAMES utf8mb4(或 utf8)任一層不一致 → 亂碼(問號 、亂碼 、雙重編碼)。
-- 建庫指定
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 建表指定
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 檢查現有編碼
SHOW VARIABLES LIKE 'character_set_%';
SHOW CREATE TABLE users;
<?php
// mysqli 物件導向(推薦)
$mysqli = new mysqli('localhost', 'user', 'pwd', 'mydb');
if ($mysqli->connect_errno) {
die("連線失敗: " . $mysqli->connect_error);
}
// 關鍵:設定連線編碼
$mysqli->set_charset('utf8mb4'); // 等同 SET NAMES utf8mb4
// PDO(推薦)
$pdo = new PDO(
'mysql:host=localhost;dbname=mydb;charset=utf8mb4',
'user', 'pwd',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
]
);
注意:PDO DSN 中的 charset=utf8mb4 (PHP 5.3.6+) 已足夠,但加上 MYSQL_ATTR_INIT_COMMAND 更保險。
<?php
// 最早送出 Header(在任何輸出前)
header('Content-Type: text/html; charset=utf-8');
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>中文測試</title>
</head>
<body>
<?php echo "中文顯示正常"; ?>
</body>
</html>
SET NAMES utf8mb4;
-- 等同於執行以下三條:
SET character_set_client = utf8mb4; -- 客戶端送出的 SQL 編碼
SET character_set_connection = utf8mb4; -- 連線層轉換編碼
SET character_set_results = utf8mb4; -- 伺服器回傳結果編碼
資料流向:
Client (UTF-8) → character_set_client → Server (utf8mb4)
Server (utf8mb4) → character_set_results → Client (UTF-8)
若中間任一環節為 latin1,中文會經過「UTF-8 → latin1 → UTF-8」不可逆轉換,導致資料損毀。
| 現象 | 可能原因 | 檢查重點 |
|---|---|---|
| 存入資料庫顯示 ??? | 連線編碼錯誤(未 SET NAMES) | $mysqli->set_charset()、PDO DSN charset |
| 讀出資料庫顯示 | 網頁輸出編碼非 UTF-8 | Header、meta charset、檔案儲存編碼 |
| 資料庫裡已是亂碼 | 寫入時連線編碼錯 | 匯出 SQL 檢查、重新匯入前修正連線 |
| Emoji 變 ???? | 資料庫用 utf8 而非 utf8mb4 | ALTER TABLE ... CHARSET=utf8mb4 |
<?php
// 批次轉換資料表編碼
$pdo = new PDO('mysql:host=localhost;dbname=old_db', 'root', 'pwd');
$tables = $pdo->query("SHOW TABLES")->fetchAll(PDO::FETCH_COLUMN);
foreach ($tables as $table) {
$pdo->exec("ALTER TABLE `{$table}` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
echo "已轉換: {$table}\n";
}
$pdo->exec("ALTER DATABASE `old_db` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
echo "資料庫編碼已更新\n";
SHOW VARIABLES LIKE 'character_set_%' 確認運行期一致遵循「三層一致」,中文亂碼將成為歷史。