如何转换格式错误的数据库字符(ascii 到 utf-8)

How to convert malformed database characters (ascii to utf-8)

我知道很多人会说这个问题已经得到了这样的回答但是让我来解释一下为什么它不是那么直截了当..

我想使用 PHP 将 "that looks like ascii" 转换为 "utf-8"

有一个网站可以做到这一点 https://onlineutf8tools.com/convert-ascii-to-utf8

当我输入这个字符串时 Z…Z 我返回 Z⬦Z 这是正确的输出。

我尝试了 iconv 和一些 mb_ 功能。虽然我不知道这些功能是否能够做我想做的事或我需要哪些选项。如果无法使用这些功能,一些自写的 PHP 代码将不胜感激。 (该网站运行 javascript 我不认为 PHP 我在这方面的能力较差)

明确一点:目标是在 PHP 中重新创建该网站正在做的事情。不要对 ascii 和 utf-8 进行语义辩论

编辑:the website uses https://github.com/mathiasbynens/utf8.js 表示

it can encode/decode any scalar Unicode code point values, as per the Encoding Standard.

标准链接到 https://encoding.spec.whatwg.org/#utf-8 所以这个库说它实现了标准,那么 PHP 呢?

UTF-8 是 ASCII 的超集,因此从 ASCII 转换为 UTF-8 就像将汽车转换为交通工具。

+--- UTF-8 ---------------+
|                         |
|   +--- ASCII ---+       |
|   |             |       |
|   +-------------+       |
+-------------------------+

您 link 的工具似乎使用术语 "ASCII" 作为 mojibake 的同义词(它说 "car" 但意思是 "scrap metal")。 Mojibake 通常以这种方式发生:

  1. 您选择了一个非英文字符: 'WHITE MEDIUM DIAMOND' (U+2B26)

  2. 您使用 UTF-8 对其进行编码:0xE2 0xAC 0xA6

  3. 您在配置为使用您所在地区广泛使用的单字节编码的工具中打开流:Windows-1252

  4. 你在单字节编码的字符table中查找UTF-8字符的各个字节:

    • 0xE2 -> â
    • 0xAC -> ¬
    • 0xA6 -> ¦
  5. 您将生成的字符编码为 UTF-8:

因此,您已将 UTF-8 流 0xE2 0xAC 0xA6 () 转换为 UTF-8 流 0xC3 0xA2 0xC2 0xAC 0xC2 0xA6 (⬦)。

要撤消此操作,您需要逆向执行这些步骤。如果您知道使用了什么代理编码(在我的示例中为 Windows-1252),那就很简单了:

$mojibake = "\xC3\xA2\xC2\xAC\xC2\xA6";
$proxy = 'Windows-1252';
var_dump($mojibake, bin2hex($mojibake));
$original = mb_convert_encoding($mojibake, $proxy, 'UTF-8');
var_dump($original, bin2hex($original));
string(6) "⬦"
string(12) "c3a2c2acc2a6"
string(3) "⬦"
string(6) "e2aca6"

但如果不这样做就很棘手。我想你可以:

  1. 编译你在不同的单字节编码中得到的不同字节序列的字典,然后使用某种贝叶斯推理来找出最可能的编码。 (我真的帮不了你。)

  2. 尝试最可能的编码并目视检查输出以确定哪个是正确的:

    // Source code saved as UTF-8
    $mojibake = "Z…Z";
    foreach (mb_list_encodings() as $proxy) {
        $original = mb_convert_encoding($mojibake, $proxy, 'UTF-8');
        echo $proxy, ': ', $original, PHP_EOL;
    }
    
  3. 如果(如您的情况)您知道原始文本是什么,并且您确定您没有混合编码,请按照#2 进行操作,但尝试 all编码PHP支持:

    // Source code saved as UTF-8
    $mojibake = 'Z…Z';
    $expected = 'Z⬦Z';
    foreach (mb_list_encodings() as $proxy) {
        $current = @mb_convert_encoding($mojibake, $proxy, 'UTF-8');
        if ($current === $expected) {
            echo "$proxy: match\n";
        }
    }
    

    (这会打印 wchar: match;不太确定那是什么意思。)