UTF-16转UTF-8的方法,防止文件有BOM头

一、前言
在读公司代码的时候,发现了一个UTF-16转UTF-8的方法,这还是博主第一次见到这种方法,不由的好奇了起来。为什么要转,应用场景是什么呢?这里大家一起来探讨下

二、贴代码
/**
* @desc UTF-16转为UTF-8编码, 必须带有BOM文件头的才可以转,
* UTF-16LE BOM文件头: [0xFF, 0xFE],
* UTF-16BE BOM文件头: [0xFE, 0xFF],
* UTF-8 BOM文件头: [0xEF, 0xBB, 0xBF]
* @param $str 这里的$str是用file_get_contents获取到的文件内容
* @return string
*/
public static function utf16_to_utf8($str)
{
$c0 = ord($str[0]);
$c1 = ord($str[1]);
$c2 = ord($str[2]);

if ($c0 == 0xFE && $c1 == 0xFF) {
// -- UTF-16BE BOM文件头: [0xFE, 0xFF],
$be = true;
} else if ($c0 == 0xFF && $c1 == 0xFE) {
// -- UTF-16LE BOM文件头: [0xFF, 0xFE],
$be = false;
} else if ($c0 == 0xEF && $c1 == 0xBB && $c2 == 0xBF) {
// -- UTF-8 BOM文件头: [0xEF, 0xBB, 0xBF]
$str = substr($str, 3);
return $str;
} else {
return $str;
}

$str = substr($str, 2);
$len = strlen($str);
$dec = '';
for ($i = 0; $i < $len; $i += 2) {
$c = ($be) ? ord($str[$i]) << 8 | ord($str[$i + 1]) :
ord($str[$i + 1]) << 8 | ord($str[$i]);
if ($c >= 0x0001 && $c <= 0x007F) {
$dec .= chr($c);
} else if ($c > 0x07FF) {
$dec .= chr(0xE0 | (($c >> 12) & 0x0F));
$dec .= chr(0x80 | (($c >> 6) & 0x3F));
$dec .= chr(0x80 | (($c >> 0) & 0x3F));
} else {
$dec .= chr(0xC0 | (($c >> 6) & 0x1F));
$dec .= chr(0x80 | (($c >> 0) & 0x3F));
}
}
return $dec;
}
从这部分代码可以看出,方法起到的主要作用是把带有BOM头的文件转换为正常的UTF-8文件。这里要转换的$str是php通过file_get_contents获取到的文件内容,可能会获取到BOM头。而BOM头对后续的操作是有很大影响的,所以这里一方面是编码转换,一方面也是为了去掉BOM头,算是考虑的更全面,也更安全一些。

PHP5中file_get_contents函数获取带BOM的utf-8文件内容

三、关于编码格式和BOM头
1、utf-16和utf-8编码

这部分大家参考这篇博客:
https://blog.csdn.net/hanbo622/article/details/52882438

2、关于BOM头

BOM是Byte Order Mark的缩写,即字节顺序标记,它是插入到UTF-8,UTF-16或UTF-32编码的Unicode文件开头的特殊标记,用来标识Unicode文件的编码类型。

BOM签名的意思就是告诉编辑器当前文件采用何种编码,方便编辑器识别,但是BOM虽然在编辑器中不显示,但是会产生输出,就像多了一个空行。一般的编码集中并不会出现bom头,unicode编码集中会出现。但对于 PHP来说,BOM是个大麻烦。

对于BOM,PHP并不会忽略,在读取、包含或者引用这些文件时,PHP会把BOM作为文件开头正文的一部分,根据嵌入式语言的特点,这串字符将被直接执行(显示)出来.这就导致了一些页面的头部总是有一条白条,尽管样式padding、margin等各方面都设置好也无法让整个网页紧贴浏览器顶部,这头部白条就是这3个不可见的字符
(0xEF 0xBB 0xBF,即BOM);另外还有的问题就是,受COOKIE送出机制的限制,在这些文件开头已经有BOM的文件中,COOKIE无法送出(因为在COOKIE送出前PHP已经送出了文件头),所以登入和登出功能失效.一切依赖COOKIE、SESSION实现的功能全部无效.所以,在编辑、修改任何文本文件的时候,请使用不会乱加BOM的编辑器.
Linux下的编辑器应该都没有这个问题.WINDOWS下,请勿使用记事本等编辑器.推荐使用Editplus,Zend studio、eclipse等编辑器。

几种编码对应的BOM:

EF BB BF UTF-8
FE FF UTF-16 (big-endian)
FF FE UTF-16 (little-endian)
00 00 FE FF UTF-32 (big-endian)
FF FE 00 00 UTF-32 (little-endian)
3、BOM头对php的影响

(1)BOM头对php文件的影响:https://www.cnblogs.com/wt645631686/p/6868826.html
(2)php的include等,会把文件的BOM头引入进来:http://blog.sina.com.cn/s/blog_62ea758a0102vvgq.html
(3)php的BOM头会造成session和cookie失效的问题:https://blog.csdn.net/ohmygirl/article/details/6931716
另外一个常见的bom头的地方时xml文件。解析失败的话,有很大一部分原因是这个。此时只要去掉bom头就行了。
(4)如果项目文件中已经有文件可能含有BOM头的话,将下面文件放在网站根目录访问即可,它会遍历当前目录下所有子目录,检测文件是否含有BOM头,并删除BOM头
https://www.cnblogs.com/lsy-ai/p/6244888.html?utm_source=itdadao&utm_medium=referral
(5)曾经我遇到的BOM头的bug问题:https://blog.csdn.net/LJFPHP/article/details/79288176
因为BOM头的存在,所以无法正常识别JSON字符串,去掉BOM头即可

这部分是BOM头对于php程序的一些影响,后面的链接都是对应的解释,可以看一看

附:
代码中使用到了一些php的函数,都是不怎么常见的函数,这里给出文档地址:

php的ord()方法:http://www.w3school.com.cn/php/func_string_ord.asp 返回字符串的首个字母的 ASCII 值。
php的chr()方法:http://www.w3school.com.cn/php/func_string_chr.asp 从不同的 ASCII 值返回字符。

四、总结
博主觉得这个转换方法也是编程的细节之一吧,对于初级程序员来说,可能根本就不会考虑到这种问题,只有在碰到这种bug了,才会去尝试解决一下。而对于大佬来说,这种规避文件BOM头的方法应该是一开始就考虑到的,然后在每次使用file_get_contents读取日志文件的时候,都调用该方法,可以很大限度的减少因为BOM头而出现的bug。也是一种编程思想吧,学到了。

end
---------------------
版权声明:本文为CSDN博主「铁柱同学」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/LJFPHP/article/details/86663927

版权声明:
作者:王金阁
链接:https://www.nnbbxx.net/post-5951.html
来源:王金阁博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
< <上一篇
下一篇>>