【问题标题】:How does php utf8_decode deal with utf8mb4? [duplicate]php utf8_decode 如何处理 utf8mb4? [复制]
【发布时间】:2022-01-23 04:20:36
【问题描述】:

我正在使用 localhost windows10 apache 2.4:Apache/2.4.51 (Win64) OpenSSL/1.1.1l PHP/8.0.11Database client version: libmysql - mysqlnd 8.0.11,它使用服务器Server version: 10.4.21-MariaDB - mariadb.org binary distribution。默认设置为 _utf8mb4:Server charset: UTF-8 Unicode (utf8mb4)

我制作了一个 php 脚本,它使用 loadHTMLFile 从维基百科页面获取内容(包括 html 标签)。然后我进一步使用xpath->query过滤dom,然后数据被mysqli_real_escape_string转义后作为字符串保存在mysql表中。稍后,我查询数据库并将内容保存在传递给loadHTML 的变量中,然后删除一些 dom 元素,然后将修改后的内容传递给saveHTML 并回显到我的网页。

会发生一些字符显示如下:

  --> Â
- --> –
€ --> €
ευρώ --> ευÏÏŽÂ

当我使用echo utf8_decode($output) 时,所有字符都正确显示。注意:不使用utf8_decode,以下任何一项都无效:

<meta charset="utf-8">  // in my html file  
header('Content-Type: text/html; charset=utf-8'); // before the echo statement      
mysqli_query($conn, "SET NAMES utf8"); // before mysql insert into and Select from statements 
mysqli_set_charset($conn, "utf8"); // before mysql insert into and Select from 

声明

同样mb_detect_encoding($output)mb_detect_encoding(utf8_decode($output)) 返回UTF-8 utf8mb4。在我的 chrome 浏览器的网络/标题选项卡中,我总是将 Content-type 设为 text/html; charset=UTF-8 ,无论我在服务器端 php/mysql 设置中进行什么更改。

我的猜测是,维基百科页面中的数据是正常的UTF-8 形式,当loadHTMLFile 下载它时,它会被php 自动转换utf8mb4。现在这些数据以utf8mb4 格式保存在mysql 表中。稍后检索时,此数据将保留为utf8mb4 格式,并以utf8mb4 格式显示给浏览器。当我使用utf8_decode 时,它必须将其转换为正常的utf-8 格式。

我猜的问题是关于utf8_decode页面的php文档,没有提到utf8mb4,而是说,多字节UTF-8 ISO-8859-1编码被转换为单字节UTF-8 ISO-8859-1。其次,文档说,ISO-8859-1 字符集不包含欧元符号。但是我的网页在utf8_decode 之后成功显示了欧元符号,并且浏览器也能够解析多字节 utf-8 字符,所以如果这是utf8_decode 所做的唯一事情,那么它应该不会对我的代码产生任何影响。

编辑:

我找到了罪魁祸首。以下回显正确的字符:

$stmt = $conn->prepare("Select ...");
...
$result = $stmt->execute();
...
$row = $stmt->get_result()->fetch_assoc()

echo $row['content']; // gives €ερυώ  

现在,$row['content'] 是直接来自我的数据库的数据,没有任何 utf_decode。但是后来我碰巧使用了 php domdocument 并且发生了以下情况:

libxml_use_internal_errors(true); // important
$content = new DOMDocument();
$content->loadHTML($row['content']);
echo $row['content'], $content->saveHTML($content); die();
// The output is:  €ερυώ
//â¬ÎµÏÏÏ

以上代码在我的查看源代码中的输出是:

€ερυώ<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>â¬ÎµÏÏÏ</p></body></html>

那么请解释一下loadHTMLsaveHTML 到底在做什么?


P.S:我的全部代码在 github repo 上可用:https://github.com/AnupamKhosla/crimeWiki 和关于 wikipedea 页面编码的特定脚本在 https://github.com/AnupamKhosla/crimeWiki/blob/main/include/wikipedea_code.phphttps://github.com/AnupamKhosla/crimeWiki/blob/main/include/post_code.php

【问题讨论】:

  • 什么版本的 MySQL? (SELECT @@version;)
  • "ISO-8859-1" 可能最接近 MySQL 的 "latin1"。
  • 现在,您在问题中发布了编辑,很明显问题不在于 mysqli。问题是您没有为 DOMDocument 指定正确的编码。见stackoverflow.com/a/47396055/1839439
  • @Dharman 有趣的是,与您提供的链接相反,当我使用$tmp = new DOMDocument(); $tmp-&gt;loadHTMLFile($url) 加载维基百科网址时,所有字符都以 utf-8 编码,并且保存在我的数据库中的数据仍然是 utf-8格式。当我使用$content = new DOMDocument(); $content-&gt;loadHTML($row['content']); 查询该数据并从中创建html dom时,我遇到了编码问题。

标签: php mysql utf-8 character-encoding utf8mb4


【解决方案1】:

使用任何编码/解码都是错误配置的表现。

当你连接到mysql时,你告诉它在客户端中使用的是什么编码。

当你声明表时,你指定如何存储东西。 CHARACTER SET utf8mb4 通常是最好的。

请提供SELECT HEX(col), col ... 以获取样本。 (你不能相信浏览器显示的内容;它会尝试“修复”编码。一旦你有了十六进制,我们就可以讨论如何修复数据。一个常见的问题是“双重编码”,其中数据被错误转换了两次.

至于您当前的样本,有足够多的不一致之处,我无法推断出哪里出了问题:

&nbsp; ->  That is represented as hex 80 by some word processors, not by HTML.
-  --> this is a plain dash; it is never mangled.  Perhaps you have an n-dash or m-dash?
€  --> mangles to "€" via "Mojibake" through latin1;
       did you omit the "SINGLE LOW-9 QUOTATION MARK" that looks like a comma??
ευρώ  --> ευÏÏŽ via "Mojibake" through latin1;

更多关于 Mojibake 和其他常见的修改:Trouble with UTF-8 characters; what I see is not what I stored

【讨论】:

  • 我做了一个示例SELECT HEX(col), col ...,它返回了所有在浏览器中正确显示的 utf8 编码。我得到了2D2D2D2D E282ACCEB5CF81CF85CF8E---- €ερυώ。我认为罪魁祸首是xpathDOMDocument。我会在我看到实际错误的主页上用十六进制值回复你。
  • 主页的问题是mysqli::query("SELECT HEX(col)")$result被输入htmlspecialcharsloadHTMLsaveHTML。所以用十六进制代码喂它们会给我错误。让我找到出路。
  • 实际上SELECT ... FROM .. 你的桌子。这是为了看看表里有什么。
  • 我确实以SELECT ... FROM ... 的身份执行了它,请在问题中查看我的编辑,我发现了破坏数据的错误点。
  • – 是 Mojibake 的“En dash”。
【解决方案2】:

utf8_decode() 帮助您的事实是偶然的。大部分时间不应使用此功能。如果使用它对您有帮助,那么它只能意味着您设法以某种方式破坏了您的数据。

utf8mb4 是 MySQL 的字符集,代表完整的 UTF-8 编码。因此,如果您在代码中到处使用 UTF-8,则永远不需要使用 utf8_decode(),因为它只会损坏数据。 ISO-8859-1 支持的字符很少。这不是你想要的。

这里似乎发生的事情是您在打开连接时忘记设置$conn-&gt;set_charset('utf8mb4')。当您未指定字符集时,许多 MySQL 服务器默认为 Latin1,这意味着即使您的架构可能一直使用 utf8mb4,但与数据库的连接不会并将数据转换为乱码。

解决方法很简单,总是在打开新的 mysqli 连接后立即设置正确的连接字符集。 $conn-&gt;set_charset('utf8mb4') 会解决你的问题,你不需要使用可笑的utf8_decode() 函数,它意外地解决了你的问题。

【讨论】:

  • 此外,€ 符号在该过程中幸存下来,因为 MySQL 在声称 Latin-1 时也存在谎言。事实上,它使用的是 Windows-1252。 (Reference)。
  • 不幸的是,$conn-&gt;set_charset('utf8mb4') 没有改变任何东西,正如我在问题中提到的那样。我的 phpmyadmin 页面提到了 Server connection collation: utf8mb4_unicode_ciServer charset: UTF-8 Unicode (utf8mb4)。此外,php ini 文件也提到了UTF-8。会不会是 loadHTMLxpath-&gt;query 在某处乱码字符编码,或者维基百科的来源不是 utf-8?
  • 不,这与phpMyAdmin无关。别看那里。只需删除所有存储的数据,设置字符集,并仔细检查您的列是否也具有正确的字符集。确保在插入数据时设置连接字符集以及在选择时设置连接字符集
  • 您好,我确实删除了所有内容,我在返回$conn 本身的函数中使用了set_charset('utf8mb4')。更重要的是,我发现了数据被破坏的地方。请参阅我的问题中的编辑部分。
猜你喜欢
  • 1970-01-01
  • 2017-02-17
  • 2020-12-07
  • 2018-04-02
  • 2014-07-29
  • 2013-12-08
  • 1970-01-01
  • 2018-12-27
  • 2016-01-23
相关资源
最近更新 更多