检测 PHP 中的混合编码并使所有内容都变为 Windows 1252

问题描述 投票:0回答:1

我有一个包含混合编码字符串的 CSV 文件:

1956;Mathé Altéry;Le Temps Perdu
1963;Alain Barrière;Elle était si jolie
2024;Gérard truc;Mon éden
2024;GÃ¥te;Ulveham
2023;Gergő Rácz;Zé Szabó
2210;Anna Mjöll;Sjúbídú
2200;Ovidijus Vyšniauskas;Lopšinė mylimai

法语名字上的一些重音符号要么是 UTF8 编码的(é ; è ),要么是用纯文本编码的('é' ASCII ?)。另外,有些字符 ('ő') 在 UTF8 中不存在,但在 Windows-1252 中不存在。

我所做的是将所有内容从 UTF8 转换为 Windows-1252:

mb_convert_encoding($text, 'Windows-1252','UTF-8');

这就像一个魅力,..除了所有被检测为 UTF8 的法语口音。

这是示例代码:

$csv = array("1956;Mathé Altéry;Le Temps Perdu",
"1963;Alain Barrière;Elle était si jolie",
"2024;Gérard truc;Mon éden",
"2024;GÃ¥te;Ulveham",
"2023;Gergő Rácz;Zé Szabó",
"2210;Anna Mjöll;Sjúbídú",
"2200;Ovidijus Vyšniauskas;Lopšinė mylimai");

foreach ($csv as $row) {
        echo "row=$row\n";
        $row_values = explode(';', $row);

        foreach ($row_values as $value) {
                echo sprintf("value=%-30s encoding=%-10s W1252=%-20s\n",
                        $value,
                        mb_detect_encoding($value),
                        mb_convert_encoding($value, 'Windows-1252' ,'UTF-8'));
        }

        echo "\n";
}

# php ./encodings.php
row=1956;Mathé Altéry;Le Temps Perdu
value=1956                           encoding=ASCII      W1252=1956
value=Mathé Altéry             encoding=UTF-8      W1252=Mathé Altéry
value=Le Temps Perdu                 encoding=ASCII      W1252=Le Temps Perdu

row=1963;Alain Barrière;Elle était si jolie
value=1963                           encoding=ASCII      W1252=1963
value=Alain Barrière              encoding=UTF-8      W1252=Alain Barrière
value=Elle était si jolie         encoding=UTF-8      W1252=Elle était si jolie

row=2024;Gérard truc;Mon éden
value=2024                           encoding=ASCII      W1252=2024
value=Gérard truc                   encoding=UTF-8      W1252=G▒rard truc
value=Mon éden                      encoding=UTF-8      W1252=Mon ▒den

row=2024;GÃ¥te;Ulveham
value=2024                           encoding=ASCII      W1252=2024
value=GÃ¥te                        encoding=UTF-8      W1252=Gåte
value=Ulveham                        encoding=ASCII      W1252=Ulveham

row=2023;Gergő Rácz;Zé Szabó
value=2023                           encoding=ASCII      W1252=2023
value=GergÅ‘ Rácz              encoding=UTF-8      W1252=Gergő Rácz
value=Zé Szabó                 encoding=UTF-8      W1252=Zé Szabó

row=2210;Anna Mjöll;Sjúbídú
value=2210                           encoding=ASCII      W1252=2210
value=Anna Mjöll                  encoding=UTF-8      W1252=Anna Mjöll
value=Sjúbídú               encoding=UTF-8      W1252=Sjúbídú

row=2200;Ovidijus Vyšniauskas;Lopšinė mylimai
value=2200                           encoding=ASCII      W1252=2200
value=Ovidijus VyÅ¡niauskas        encoding=UTF-8      W1252=Ovidijus Vyšniauskas
value=LopÅ¡inÄ— mylimai         encoding=UTF-8      W1252=Lopšinė mylimai

您可以看到具有法语口音的值被检测为 UTF-8,并将它们转换为 W1252 会添加奇怪的字符,这意味着转换错误。我知道 W1252 不利于国际化,但是将“LopÅ¡inä— mylimai”转换为 UTF8 不会给出正确的结果,只有 W1252 才能给出正确的结果。

现在,我不知道如何检测哪些字符串需要编码,哪些不需要编码。 mb_detect_encoding 似乎没有返回可靠的结果。

有什么想法吗?

非常感谢大家!!

php encoding mb-convert-encoding
1个回答
0
投票

这种(混合)mojibake案例可能非常烦人......我找到了以下解决方案:

<?php
mb_substitute_character(0xFFFD);    // replacement character instead of `?`
$mojibakeCP = 'Windows-1252';

$csv = array(
"1956;Mathé Altéry;Le Temps Perdu",
"1963;Alain Barrière;Elle était si jolie",
"2024;Gérard truc;Mon éden",
"2024;GÃ¥te;Ulveham",
"2023;Gergő Rácz;Zé Szabó",
"2210;Anna Mjöll;Sjúbídú",
"2200;Ovidijus Vyšniauskas;Lopšinė mylimai",
"1;𑇖 🢪 🤪 惦 榙 晦 晀 é Ù 😃;",
"2;𑇖 🢪 🤪 惦 榙 晦 晀 é Ù 😃;",
"3;Ã\u{0081} Ã\u{008D} Ã\u{008F} Ã\u{0090} Ã\u{009D};",   // controls in string
"4;Á Í Ï Ð Ý;",
'5;nothing to convert in pure ASCII;',
"6;Maßüne Süßen Мlaߜn;",
"7;Süßen;",              // a real German word (a town in southern Germany)
"8;Maßüne;",             // a plausible fictional word
"9;Ðœlaßœn;"             // a plausible fictional word, false mojibake
);

foreach ($csv as $row) {
    $converted = mb_convert_encoding($row, $mojibakeCP ,'UTF-8');
    if ( $converted == $row) {
        $realmojibake = 0;
    } else {
        $realmojibake = 1;
        foreach ( mb_str_split($converted) as $convertedchar ){
            if ( mb_ord($convertedchar)==false) {
                $realmojibake = 0;
                break;
            }
        }
    }
    if ($realmojibake) {
        echo sprintf( "%-50s (%s)", $converted, $row) . PHP_EOL;
    } else {
        echo $row . PHP_EOL;
    }
}
?>

输出

.\mixed_mojibake.php

1956;Mathé Altéry;Le Temps Perdu                 (1956;Mathé Altéry;Le Temps Perdu)
1963;Alain Barrière;Elle était si jolie          (1963;Alain Barrière;Elle était si jolie)
2024;Gérard truc;Mon éden
2024;Gåte;Ulveham                                 (2024;GÃ¥te;Ulveham)
2023;Gergő Rácz;Zé Szabó                       (2023;GergÅ‘ Rácz;Zé Szabó)
2210;Anna Mjöll;Sjúbídú                        (2210;Anna Mjöll;Sjúbídú)
2200;Ovidijus Vyšniauskas;Lopšinė mylimai       (2200;Ovidijus VyÅ¡niauskas;LopÅ¡inÄ— mylimai)
1;𑇖 🢪 🤪 惦 榙 晦 晀 é Ù 😃;       (1;𑇖 🢪 🤪 惦 榙 晦 晀 é Ù 😃;)
2;𑇖 🢪 🤪 惦 榙 晦 晀 é Ù 😃;
3;Á Í Ï Ð Ý;                                  (3;Ã Ã Ã Ã Ã;)
4;Á Í Ï Ð Ý;
5;nothing to convert in pure ASCII;
6;Maßüne Süßen Мlaߜn;                        (6;Maßüne Süßen Ðœlaßœn;)
7;Süßen;
8;Maßüne;
9;Мlaߜn;                                         (9;Ðœlaßœn;)

注意:我已在您的数据中添加了行(在第一列中带有

1
..
9
)以进行调试。
输出显示假 mojibake 检测似乎(在某种意义上)不完整:在最后一行中,
Ðœlaßœn
(这个虚构的单词看起来几乎是基于拉丁字母的单词),并且上面的算法无法识别 false mojibake 并将其翻译为
Мlaߜn
。事实上:

  • Ðœlaßœn
    是:

    • Ð
      (U+00D0,拉丁大写字母 Eth
    • œ
      (U+0153,拉丁小连字 Oe
    • l
      (U+006C,拉丁小写字母 L
    • a
      (U+0061,拉丁小写字母 A
    • ß
      (U+00DF,拉丁文小写字母升 S
    • œ
      (U+0153,拉丁小连字 Oe
    • n
      (U+006E,拉丁小写字母 N
  • Мlaߜn
    是:

    • М
      (U+041C,西里尔大写字母 Em
    • l
      (U+006C,拉丁小写字母 L
    • a
      (U+0061,拉丁小写字母 A
    • ߜ
      (U+07DC,Nko 字母 Gba
    • n
      (U+006E,拉丁小写字母 N
© www.soinside.com 2019 - 2024. All rights reserved.