PHP 检测文件编码的不完美解决方案

因为某些原因现在需要批量检测文件编码,看看是不是有非 UTF-8 文件混在其中
我当然是首选了我最熟悉的 PHP,感觉应该很简单

google 搜索 php detect encoding,第一个就是 PHP 官方文档 mb_detect_encoding – Manual – PHP
于是按照文档有样学样,拿个 UTF-8 文件测试一下

echo mb_detect_encoding( file_get_contents('./utf8.txt') );

当我满心以为会显示 UTF-8 的时候,看到的却是 ASCII

几经核实、尝试,发现即便我传个 'test' 进去都会返回 ASCII。只有字符串中包含汉字等非 ASCII 字符时才会返回 UTF-8。围绕这个函数搜索了一下,也看到不少说这玩意儿不靠谱的言论。不能说它错,但返回值确实不符合直觉

继续找方案,甚至还发现了用 BOM 来判断是否 UTF-8 的操作,这个更不靠谱😂
除此之外见得最多的主流(抄来抄去)解决方案是

 function detect_encoding($file) {
     $list = array('GBK', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'ISO-8859-1');
     $str = file_get_contents($file);
     foreach ($list as $item) {
         $tmp = mb_convert_encoding($str, $item, $item);
         if (md5($tmp) == md5($str)) {
             return $item;
         }
     }
     return null;
 }

通过对比转换前后的字符串是否相等来判断编码,也确实是个办法
不过对于我现在只需要判断是否 UTF-8 的需求来说没这个必要

我又把目光放回到 mb_detect_encoding,注意到它的第二个参数 encoding_list

当被测字符串的编码不在这个参数之中时函数返回 false

这个参数的默认值为 mb_detect_order() ,经测试该函数的返回值为 ['ASCII', 'UTF-8']
噢,我好像破案了 —— 当文件同时满足多种条件时,函数只返回第一个编码名称

因此只要将 UTF-8 放到最前面,或者只提供 UTF-8 即可

8 条评论

昵称
  1. Kenvix

    但是PHP说的也确实没毛病,UTF-8本来就向下兼容ASCII,从二进制角度看也完全相等,把一个纯ASCII文件直接当作UTF-8编码就好了

  2. xiaobeii

    我之前用来处理txt文本转excel乱码的时候用的,也都是网上抄的,能用就行- –
    function characet($data)
    {
    if (!empty($data))
    {
    $fileType = mb_detect_encoding($data, array(‘UTF-8’, ‘GBK’, ‘LATIN1’, ‘BIG5’));
    if ($fileType != ‘UTF-8’)
    {
    $data = mb_convert_encoding($data, ‘utf-8’, $fileType);
    }
    }
    return $data;
    }

  3. weapon_zhang

    我在PHP官方文档看到了这么一句话,15年前的
    If you need to distinguish between UTF-8 and ISO-8859-1 encoding, list UTF-8 first in your encoding_list:
    mb_detect_encoding($string, ‘UTF-8, ISO-8859-1’);
    if you list ISO-8859-1 first, mb_detect_encoding() will always return ISO-8859-1.

  4. weapon_zhang

    另辟蹊径,方法独到,佩服佩服

  5. 惶心

    哈哈哈哈哈哈我以为只有我会这样 – 想用某语言实现某功能:Google 搜索 {lang-name} + {target}

    1. mokeyjay

      这不是基本操作吗😆

    2. nothing

      +1