PHP 来自 MySQL 数据库的 UTF-8 字符串的输出编码问题
PHP output encoding issues with UTF-8 strings from MySQL databases
我知道这个问题一直以一种或另一种形式出现在这里,但我对如何解决它有点不知所措。我有一个 PHP 网站,它位于 MySQL 的 运行 之外,它显示一些扩展字符是乱码。据我所知,从内容导入到在屏幕上显示的每一步,它都被编码为 UTF-8。尽管如此,它仍然显示出奇怪的编码问题。这是第一个测试示例(Natural Phënåm¥na,这是故意的),mb_detect_encoding
识别为 UTF-8,我只能使用 utf8_decode
:
才能正确显示
no utf8_decode: Natural Phënåm¥na
utf8_decode: Natural Phënåm¥na
第二个例子,从来没有 utf8_decodes 正确(应该是 ümlaut 和“排版引号”(故意添加的扩展字符,作为测试:
no utf8_decode: This pürson from “Vancouver, Canadaâ€
utf8_decode: This pürson from �??Vancouver, Canada�?�
我最初的想法是它是双重编码的,但我认为这不是正在发生的事情。当我在命令行上查询时,一切都在 MySQL 中正确显示。
以下是我调查过的所有内容的概要:
- 导入的内容经验证为 UTF-8,使用 UTF-8 连接导入到 MySQL
- MySQL数据库,表,列都是UTF-8,utf_unicode_*
- character_set_client,MySQL 中的等变量在 Amazon RDS
上设置为 utf8
- PHP PDO 连接为 UTF-8,NAME 设置为 UTF-8
- PHP header 字符集和 HTML 元字符集都是 UTF-8
- mb_detect_encoding 正在为两个字符串返回 UTF-8
经过几个小时的故障排除,我有点不知所措。一时兴起,我什至尝试将 HTML header/meta 和 PHP header 设置为 ISO-8559-1,但这也没有用。
我最近花了一段时间与 Amazon RDS 进行斗争以获得正确的变量集,但除此之外我没有想法。
mysql> show variables like '%character%';
+--------------------------+-------------------------------------------+
| Variable_name | Value |
+--------------------------+-------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | utf8 |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /rdsdbbin/mysql-5.5.40.R1/share/charsets/ |
+--------------------------+-------------------------------------------+
所以我想知道,是否缺少某些步骤?有什么明显的吗?提前致谢。
更新
这是我的 PHP 输出脚本,用于进一步说明我提到的 "output":
<?php header("Content-type: text/html; charset=utf-8"); ?>
<html>
<header>
<meta charset="utf-8" />
<title>My test</title>
</header>
<body>
<?php
try {
$dbh = new PDO("mysql:host=localhost;dbname=database",
"user", "password", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
}
catch(PDOException $e) {
echo $e->getMessage();
}
$sth = $dbh->prepare("my select statement");
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
echo mb_detect_encoding($row['name']);
echo "<br>no utf8 decode: ". $row['name'] . "<br>\n";
echo "single utf8 decode: ". utf8_decode($row['name']) . "<br>\n";
echo "no utf8 decode: ". $row['description'] . "<br>\n";
echo "single utf8 decode: ". (utf8_decode($row['description'])) . "<br>\n";
}
?>
</body>
</html>
更新#2
我还尝试将这些相同的字符直接从 PHP 回显和直接静态 HTML 输出到浏览器中,字符显示得非常好。
echo "“test ü ö”<br>"; ?>
<p>“test ü ö”</p>
你提到它在所有数据流中都是 utf-8,除非它在屏幕上呈现。我假设在浏览器上,而不是在控制台上。如果是这样,请检查 html 是否在 <head>
标签内包含 <meta charset="utf-8">
。就像 html5 样板 https://github.com/h5bp/html5-boilerplate/blob/master/dist/index.html
所以看起来在 MySQL 级别上,它在其中一些字段中对 UTF-8 字符进行了双重编码。我终于能够通过这个很棒的博客 post Getting out of MySQL Character Set Hell 确定它。当它从 Python 发送时,或者当它到达 PHP API 时,不是 100% 清楚它是 "double-encoded",但它是答案的 90%,就在那里。
您不应更改所有 character_set%
字段,只需更改受 SET NAMES utf8;
.
影响的三个字段
不要使用 utf8_encode 或解码。
你存储的时候可能搞砸了。
这似乎可以恢复字符,但这不是一个可行的修复方法:
CONVERT(CAST(CONVERT('pürson from “Vancouver, Canadaâ€' USING latin1)
AS BINARY)
USING utf8)
--> 'pürson from “Vancouver, Canada - spec',
为了弄清楚做了什么,请提供
SELECT col, HEX(col) FROM tbl WHERE ...
对于某些未正确呈现的单元格。
我知道这个问题一直以一种或另一种形式出现在这里,但我对如何解决它有点不知所措。我有一个 PHP 网站,它位于 MySQL 的 运行 之外,它显示一些扩展字符是乱码。据我所知,从内容导入到在屏幕上显示的每一步,它都被编码为 UTF-8。尽管如此,它仍然显示出奇怪的编码问题。这是第一个测试示例(Natural Phënåm¥na,这是故意的),mb_detect_encoding
识别为 UTF-8,我只能使用 utf8_decode
:
no utf8_decode: Natural Phënåm¥na
utf8_decode: Natural Phënåm¥na
第二个例子,从来没有 utf8_decodes 正确(应该是 ümlaut 和“排版引号”(故意添加的扩展字符,作为测试:
no utf8_decode: This pürson from “Vancouver, Canadaâ€
utf8_decode: This pürson from �??Vancouver, Canada�?�
我最初的想法是它是双重编码的,但我认为这不是正在发生的事情。当我在命令行上查询时,一切都在 MySQL 中正确显示。
以下是我调查过的所有内容的概要:
- 导入的内容经验证为 UTF-8,使用 UTF-8 连接导入到 MySQL
- MySQL数据库,表,列都是UTF-8,utf_unicode_*
- character_set_client,MySQL 中的等变量在 Amazon RDS 上设置为 utf8
- PHP PDO 连接为 UTF-8,NAME 设置为 UTF-8
- PHP header 字符集和 HTML 元字符集都是 UTF-8
- mb_detect_encoding 正在为两个字符串返回 UTF-8
经过几个小时的故障排除,我有点不知所措。一时兴起,我什至尝试将 HTML header/meta 和 PHP header 设置为 ISO-8559-1,但这也没有用。
我最近花了一段时间与 Amazon RDS 进行斗争以获得正确的变量集,但除此之外我没有想法。
mysql> show variables like '%character%';
+--------------------------+-------------------------------------------+
| Variable_name | Value |
+--------------------------+-------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | utf8 |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /rdsdbbin/mysql-5.5.40.R1/share/charsets/ |
+--------------------------+-------------------------------------------+
所以我想知道,是否缺少某些步骤?有什么明显的吗?提前致谢。
更新
这是我的 PHP 输出脚本,用于进一步说明我提到的 "output":
<?php header("Content-type: text/html; charset=utf-8"); ?>
<html>
<header>
<meta charset="utf-8" />
<title>My test</title>
</header>
<body>
<?php
try {
$dbh = new PDO("mysql:host=localhost;dbname=database",
"user", "password", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
}
catch(PDOException $e) {
echo $e->getMessage();
}
$sth = $dbh->prepare("my select statement");
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
echo mb_detect_encoding($row['name']);
echo "<br>no utf8 decode: ". $row['name'] . "<br>\n";
echo "single utf8 decode: ". utf8_decode($row['name']) . "<br>\n";
echo "no utf8 decode: ". $row['description'] . "<br>\n";
echo "single utf8 decode: ". (utf8_decode($row['description'])) . "<br>\n";
}
?>
</body>
</html>
更新#2 我还尝试将这些相同的字符直接从 PHP 回显和直接静态 HTML 输出到浏览器中,字符显示得非常好。
echo "“test ü ö”<br>"; ?>
<p>“test ü ö”</p>
你提到它在所有数据流中都是 utf-8,除非它在屏幕上呈现。我假设在浏览器上,而不是在控制台上。如果是这样,请检查 html 是否在 <head>
标签内包含 <meta charset="utf-8">
。就像 html5 样板 https://github.com/h5bp/html5-boilerplate/blob/master/dist/index.html
所以看起来在 MySQL 级别上,它在其中一些字段中对 UTF-8 字符进行了双重编码。我终于能够通过这个很棒的博客 post Getting out of MySQL Character Set Hell 确定它。当它从 Python 发送时,或者当它到达 PHP API 时,不是 100% 清楚它是 "double-encoded",但它是答案的 90%,就在那里。
您不应更改所有 character_set%
字段,只需更改受 SET NAMES utf8;
.
不要使用 utf8_encode 或解码。
你存储的时候可能搞砸了。
这似乎可以恢复字符,但这不是一个可行的修复方法:
CONVERT(CAST(CONVERT('pürson from “Vancouver, Canadaâ€' USING latin1)
AS BINARY)
USING utf8)
--> 'pürson from “Vancouver, Canada - spec',
为了弄清楚做了什么,请提供
SELECT col, HEX(col) FROM tbl WHERE ...
对于某些未正确呈现的单元格。