You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用PHP修复CSV文件中的乱码(Mojibake)问题?

如何使用PHP修复CSV文件中的乱码(Mojibake)问题?

从你描述的情况来看,这种乱码是典型的双重编码错误导致的——原始的UTF-8文本被错误地用单字节编码(比如Windows-1252/ISO-8859-1)解码,之后又被当作单字节编码的文本重新编码为UTF-8,最终形成了像Ã?Æ?Ã?â??这类多层混乱的字符组合。下面给你一步步的解决方案:

一、先理解乱码的修复逻辑

要修复这种双重编码的乱码,需要反向操作两次错误的编码流程:

  1. 先把当前的UTF-8乱码字符串,解码为Windows-1252/ISO-8859-1的字节流(还原第一次错误解码前的原始字节)
  2. 再将这些字节流当作真正的UTF-8文本重新解码

二、修改修复乱码的方法

更新你的fixMojibake方法,加入双重转换的逻辑:

private function fixMojibake(array &$line): void
{
    foreach ($line as $key => $value) {
        if (is_string($value) && Str::isMojibake($value)) {
            // 优先尝试Windows-1252的双重转换
            $rawBytes = mb_convert_encoding($value, 'Windows-1252', 'UTF-8');
            $fixedValue = mb_convert_encoding($rawBytes, 'UTF-8', 'Windows-1252');
            
            // 如果Windows-1252修复无效,试试ISO-8859-1的组合
            if (Str::isMojibake($fixedValue)) {
                $rawBytes = mb_convert_encoding($value, 'ISO-8859-1', 'UTF-8');
                $fixedValue = mb_convert_encoding($rawBytes, 'UTF-8', 'ISO-8859-1');
            }
            
            $line[$key] = $fixedValue;
        }
    }
}

如果mb_convert_encoding效果不好,也可以试试iconv(对边缘编码场景的处理更严格):

// 用iconv实现双重转换
$fixedValue = iconv('UTF-8', 'Windows-1252//IGNORE', $value);
$fixedValue = iconv('Windows-1252', 'UTF-8//IGNORE', $fixedValue);

三、优化乱码检测的正则表达式

你当前的正则/Ã.|â.|Â./可能会误判一些正常字符,建议优化为更精准的匹配,只针对典型的双重编码乱码:

public static function isMojibake(string $string): bool
{
    // 匹配常见的双重编码乱码模式:Ã+特殊字符、â+特殊字符、Â+特殊字符
    return preg_match('/Ã[‒”“]|â[€’”“]|Â[€’”“,.]/', $string) === 1;
}

四、确保CSV读取的编码正确

虽然file -i显示CSV是us-ascii,但实际上文件包含非ASCII字符,读取时不要依赖系统默认编码:

方式1:用二进制模式读取

避免系统自动转换编码,保留原始字节流:

$handle = fopen('sample.csv', 'rb');
while (($line = fgetcsv($handle)) !== false) {
    $this->fixMojibake($line);
    // 处理修复后的行数据
}
fclose($handle);

方式2:用SplFileObject指定编码

如果用SplFileObject读取,可以强制指定编码尝试读取:

$file = new SplFileObject('sample.csv', 'r');
$file->setFlags(SplFileObject::READ_CSV);
// 先尝试UTF-8,不行再换成Windows-1252
$file->setEncoding('UTF-8'); 

foreach ($file as $line) {
    $this->fixMojibake($line);
    // 处理行数据
}

五、验证修复效果

针对你给出的示例乱码字符串:
Krog StreetÃ?Æ?Ã?â??Ã?â? Ã¢â?¬â?¢Ã?Æ?Ã?¢Ã?¢ââ?¬Å¡Ã?¬Ã?â?¦Ã?¡Ã?Æ?Ã?â??Ã?¢ââ??¬Ã?¡Ã?Æ?ââ?¬Å¡Ã?â??Ã?  NE ( Krog Street market)

用上述方法修复后,应该能还原为类似:
Krog Street ’… NE (Krog Street market)(具体取决于原始的特殊字符,比如智能引号、省略号等)

如果还是不行,可以试试替换编码候选为CP1254(土耳其编码)或者ISO-8859-15,不过Windows-1252是这类乱码最常见的根源编码。

火山引擎 最新活动