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

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁柱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值