【金沙js333娱乐场】PHP中碰到BOM、编码导致json_decode函数不可能深入分析难题

昨天同事遇到一个奇怪的问题,就是以下代码,无法通过JSON校验,也无法通过PHP的json_decode函数解析。

下图是出现前面说的情况后用firebug看到的HTML代码。
金沙js333娱乐场 1
图1

=======先介绍下BOM==============

复制代码 代码如下:

里面莫名其妙多出了一个空白行,而我们看源代码里面却没有。

Bytes  Encoding Form
EF BB BF UTF-8
FF FE UTF-16 aka UCS-2, little endian
FE FF UTF-16 aka UCS-2, big endian
00 00 FF FE UTF-32 aka UCS-4, little endian.
00 00 FE FF UTF-32 aka UCS-4, big-endian.

[
    {
        ”title”: “”,
        “pinyin”: “”
    }
]

我最常用的办法,利用php替换

=======================

可能聪明的你已经猜到其中包含有不看见的特殊字符,在vim下查看:

BOM: 万国码档案签名 BOM (Byte Order Mark, U+FEFF)

读取 unicode csv 文件

复制代码 代码如下:

BOM 的内容可以表示 UNICODE 是哪种编码, 但是在接收到的档案, 要拆解后写入
DB, 看到 BOM 就觉得有点 ooxx.

function
fopen_utf8($filename){
    $encoding=”;
    $handle = fopen($filename, ‘r’);
    $bom = fread($handle, 2);
//    fclose($handle);
    rewind($handle);
 
    if($bom === chr(0xff).chr(0xfe)  || $bom === chr(0xfe).chr(0xff)){
            // UTF16 Byte Order Mark present
            $encoding = ‘UTF-16’;
    } else {
        $file_sample = fread($handle, 1000) + ‘e’; //read first 1000
bytes
        // + e is a workaround for mb_string bug
        rewind($handle);
 
        $encoding =
mb_detect_encoding()($file_sample
, ‘UTF-8, UTF-7, ASCII, EUC-JP,SJIS, eucJP-win, SJIS-win, JIS,
ISO-2022-JP’);
    }
    if ($encoding){
        stream_filter_append($handle,
‘convert.iconv.’.$encoding.’/UTF-8′);
    }
    return  ($handle);
}

[
    {
        <feff>”title”: “”,
        “pinyin”: “”
    }
]

在 utf8_encode 看到两段程式可以来测试 写入/移除 BOM.

生成 unicode csv (此php文件一定要是无BOM的UTF-8编码文件)

发现在“title”前面有一个字符<feff>,如果你之前了解过BOM,应该知道这个特殊字符就是BOM,关于其介绍可以参考另一篇文章:计算机中的字符串编码、乱码、BOM等问题详解.

将写入的档案内容前加 BOM

?View Code PHP
$content=iconv(“UTF-8″,”UTF-16LE”,$content);
$content = “\xFF\xFE”.$content; //添加BOM
header(“Content-type: text/csv;charset=UTF-16LE”) ;
header(“Content-Disposition: attachment; filename=test.csv”);
 
再介绍一个 操作 ANSI 编码 以 “,” 隔开的 操作类
 
<?php
// Unicode BOM is U+FEFF, but after encoded, it will look like this.
define (‘UTF32_BIG_ENDIAN_BOM’   , chr(0x00) . chr(0x00) . chr(0xFE)
. chr(0xFF));
define (‘UTF32_LITTLE_ENDIAN_BOM’, chr(0xFF) . chr(0xFE) . chr(0x00)
. chr(0x00));
define (‘UTF16_BIG_ENDIAN_BOM’   , chr(0xFE) . chr(0xFF));
define (‘UTF16_LITTLE_ENDIAN_BOM’, chr(0xFF) . chr(0xFE));
define (‘UTF8_BOM’               , chr(0xEF) . chr(0xBB) .
chr(0xBF));
 
 function detect_utf_encoding($filename) {
 
     $text = file_get_contents($filename);
     $first2 = substr($text, 0,
2);
     $first3 = substr($text, 0, 3);
     $first4 = substr($text, 0, 3);
 
     if ($first3 == UTF8_BOM) return ‘UTF-8’;
     elseif ($first4 == UTF32_BIG_ENDIAN_BOM) return ‘UTF-32BE’;
     elseif ($first4 == UTF32_LITTLE_ENDIAN_BOM) return ‘UTF-32LE’;
     elseif ($first2 == UTF16_BIG_ENDIAN_BOM) return ‘UTF-16BE’;
     elseif ($first2 == UTF16_LITTLE_ENDIAN_BOM) return ‘UTF-16LE’;
 }
?>

在Linux下通过xxd命令查看文件内容的十六进制:

 代码如下

复制代码 代码如下:

<?php
function writeUTF8File($filename,$content)
{
    $f = fopen($filename,
‘w’);
    fwrite($f, pack(“CCC”, 0xef,0xbb,0xbf));
    fwrite($f,$content);
    fclose($f);
}
?>

0000000: 5b 0a 20 20 20 20 7b 0a 20 20 20 20 20 20 20 20  [.   
{.       
0000010: ef bb bf 22 74 69 74 6c 65 22 3a 20 22 22 2c 0a  …”title”:
“”,.
0000020: 20 20 20 20 20 20 20 20 22 70 69 6e 79 69 6e 22         
“pinyin”
0000030: 3a 20 22 22 0a 20 20 20 20 7d 0a 5d 0a           : “”.    }.].

移除 BOM function

可以看到刚才那个”title”前面的特殊字符十六进制为:ef bb
bf,正是标记UTF-8的BOM。BOM的含义如下:

 代码如下

复制代码 代码如下:

<?php
function removeBOM($str = ”)
{
   if (substr($str, 0,3) ==
pack(“CCC”,0xef,0xbb,0xbf)) {
       $str = substr($str, 3);
   }
   return $str;
}
?>

开头字节            Charset/encoding
EF BB BF        UTF-8
FE FF           UTF-16/UCS-2, little endian(UTF-16LE)
FF FE           UTF-16/UCS-2, big endian(UTF-16BE)
FF FE 00 00     UTF-32/UCS-4, little endian.
00 00 FE FF     UTF-32/UCS-4, big-endia

由此上述 BOM = pack(“CCC”,0xef,0xbb,0xbf), 所以移除 BOM 的写法可用上面的
removeBOM function 或 下述其一:

发现问题解决就很容易了,查找删除BOM就OK了,linux下BOM相关的命令有:

■str_replace(“锘�”, ”, $bom_content);
■preg_replace(“/^锘�/”, ”, $bom_content);
另外看到 判断此字串是不是 UTF-8 的 function:

VIM的BOM操作

 代码如下

复制代码 代码如下:

function isUTF8($string)
{
    return (utf8_encode(utf8_decode($string)) == $string);
}

#添加BOM
:set bomb
#删除BOM
:set nobomb
#查询BOM
:set bomb?

linux系统中使用shell来解决

查找UTF-8编码中的BOM

在详细讨论UTF-8编码中BOM的检测与删除问题前,不妨先通过一个例子热热身:

发表评论

电子邮件地址不会被公开。 必填项已用*标注