介绍:另一个不便之真相
无法以100%的准确度和/或信心检测未知文本的编码。
实际上,可能会出现各种可能结果的情况:您可以非常确定UTF-8中的多语言文本将被正确地检测为这样,而无法检测到ISO-8859编码系列中哪个对应于某些文本--除非您愿意进行统计分析,否则甚至不可能做出有根据的猜测!
我们有什么可以使用的工具?
解决了以上问题后,让我们看看你能做些什么。首先,除非你带来自定义工具,否则你受限于mb_detect_encoding
所能为你提供的内容。不幸的是,那不算太多。姊妹函数mb_detect_order
的文档说明如下:
mbstring目前实现了以下编码检测过滤器。如果以下编码存在无效字节序列,则编码检测将失败:UTF-8、UTF-7、ASCII、EUC-JP、SJIS、eucJP-win、SJIS-win、JIS、ISO-2022-JP。对于ISO-8859-X,mbstring总是检测为ISO-8859-X。对于UTF-16、UTF-32、UCS2和UCS4,编码检测将始终失败。因此,除了日语编码外,您基本上可以区分UTF-8、UTF-7和ASCII。您无法检测ISO-8859-X,因为任何文本都将被“识别”为这些编码之一(即您将具有100%的误报率-不好),而包括UTF-16在内的另一组编码根本不受支持。
不幸的是,坏消息并没有结束。 编码顺序也很重要! 由于使用UTF-7或ASCII编码的文本也是有效的UTF-8,将UTF-8放在候选列表的最前面将确保您只会得到这种结果--因此必须尽一切可能避免。
由于默认检测顺序取决于php.ini setting,您绝不能依赖它,并通过设置自己的检测顺序进入已知状态:
mb_detect_order('ASCII, UTF-8')
所以你至少可以判断你的文本是ASCII还是UTF-8,对吗?嗯,并不是。除非你明确要求在说“UTF-8”时,“你真的是指它”。
$valid_utf8 = "\xC2\xA2";
$invalid_utf8 = "\xC2\x00";
mb_detect_order('UTF-8');
echo mb_detect_encoding($valid_utf8); // "utf-8": correct
echo mb_detect_encoding($invalid_utf8); // "utf-8": WTF?!?!?!
上述问题在于,除非您为$strict参数传递true,否则检测UTF-8会有点过于乐观。
那么,您实际上可以用这个东西做什么呢?
这是最好的方法——正确地检测编码(勉强使用复数形式)。
$valid_utf8 = "\xC2\xA2";
$invalid_utf8 = "\xC2\x00";
$ascii = "hello world";
mb_detect_order('ASCII, UTF-8');
echo mb_detect_encoding($valid_utf8, mb_detect_order(), true); // OK: "utf-8"
echo mb_detect_encoding($invalid_utf8, mb_detect_order(), true); // OK: false
echo mb_detect_encoding($ascii, mb_detect_order(), true); // OK: "ascii"
如何处理无效的UTF-8文本?
除非你已经掌握了该文本的其他信息,否则很遗憾没有任何方法。
好吧,这并不完全正确。实际上有一些事情可以做:
- 查看文本开头是否有BOM。可能没有,即使有,从数学上讲,你也可能将单字节编码误认为是Unicode,但值得一试。
- 查看它是否是UTF-16的一种变体。如果偶数位字节的大多数具有相同的值,则很可能是UTF-16 LE。如果奇数位字节的大多数都是相同的值,则很可能是UTF-16 BE。不幸的是,在这两种情况下,你永远无法确定。
- 假设文本采用ISO-8859-X编码,并根据对应于此编码的脚本的已知属性进行统计分析,以查看结果是否接近预期。如果对于某些编码而言足够接近,而对于其他编码而言则差距很大,那么你可以进行有根据的猜测。