原文地址:http://levi.cg.am/archives/2563
GDC注:该篇文章很好,立马就解决了我的问题。建议看原文,复制过来,有些格式不太好看。
问题:用php读取文件中的json数据,怎么解析都是返回null。
1
|
{
"a"
:1,
"b"
:2,
"x"
:[{
"c"
:3},{
"d"
:4},{
"e"
:5}]}
|
读取文件,使用了file_get_contents函数。
1
2
3
4
|
$json
=
'{"a":1,"b":2,"x":[{"c":3},{"d":4},{"e":5}]}'
;
var_dump(json_decode(
$json
));
var_dump(json_decode(
$json
, true));
|
如果直接在php中读取json字符串,是没有错的,怎么从文件读取就错了呢。
php5.2以后自带json_decode函数,但是对json文本串的格式要求非常严格。
很可能使用该函数得到的返回值是NULL
使用json_last_error()函数获取到的返回值是JSON_ERROR_SYNTAX(Syntax error)。
可以通过以下几个方式排错。
- json字符串必须以双引号包含
1$output
=
str_replace
(
"'"
, '"',
$output
);
- json字符串必须是utf8编码
1
$output
= iconv(
'gbk'
,
'utf8'
,
$output
);
- 不能有多余的逗号 如:[1,2,],用正则替换掉
1
preg_replace(
'/,\s*([\]}])/m'
,
'$1'
,
$output
)
- 数组对象是需要用print_r ,不能用echo
- 之后还有乱码情况 ..我的个案是这样的.
1
header(
"Content-Type: text/html; charset=UTF-8"
);
造成json_decode() 解析null的原因关键结果在是,
- json文件是UTF-8格式
- 带有BOM
修正后代码如下,即可正常解析
1
2
3
4
5
6
7
8
9
10
11
12
13
|
$dmText
=
file_get_contents
( AROOT .
'data'
. DS .
'DMType.json.php'
);
if
(preg_match(
'/^\xEF\xBB\xBF/'
,
$dmText
))
{
$dmText
=
substr
(
$dmText
,3);
}
//trim
$dmText
= t(
$dmText
);
echo
$dmText
;
/* create array list from comments */
$dmList
= json_decode(
$dmText
,true);
//当该参数为 TRUE 时,将返回 array 而非 object 。
var_dump(
$dmList
);
|
显示结果:
View Code { "success": "true", "total":"4", "items": [ {"id":"1","c":"asdaEG","tb": "dm_suppliertype", "cn": "供应商类型","tips":"供应商类型"}, {"id":"2","c":"adsafR","tb": "suppliertype2", "cn": "供应商类型2","tips":"供应商类型"}, {"id":"3","c":"ada222","tb": "suppliertype3", "cn": "供应商类型3","tips":"供应商类型"}, {"id":"4","c":"23jetG","tb": "suppliertype4", "cn": "供应商类型4","tips":"供应商类型"} ] }array(3) { ["success"]=> string(4) "true" ["total"]=> string(1) "4" ["items"]=> array(4) { [0]=> array(5) { ["id"]=> string(1) "1" ["c"]=> string(6) "asdaEG" ["tb"]=> string(15) "dm_suppliertype" ["cn"]=> string(15) "供应商类型" ["tips"]=> string(15) "供应商类型" } [1]=> array(5) { ["id"]=> string(1) "2" ["c"]=> string(6) "adsafR" ["tb"]=> string(13) "suppliertype2" ["cn"]=> string(16) "供应商类型2" ["tips"]=> string(15) "供应商类型" } [2]=> array(5) { ["id"]=> string(1) "3" ["c"]=> string(6) "ada222" ["tb"]=> string(13) "suppliertype3" ["cn"]=> string(16) "供应商类型3" ["tips"]=> string(15) "供应商类型" } [3]=> array(5) { ["id"]=> string(1) "4" ["c"]=> string(6) "23jetG" ["tb"]=> string(13) "suppliertype4" ["cn"]=> string(16) "供应商类型4" ["tips"]=> string(15) "供应商类型" } } }
附录:
1、PHP5中file_get_contents函数获取带BOM的utf-8文件内容时需注意
PHP5中的file_get_contents函数获取文件内容,实际是按二进制来读取的,所以,当你用file_get_contents去获取一个带BOM的UTF-8文件时,它并不会把UTF-8的BOM去掉,当你把读取的内容当作文本内容来进行一些操作时,可能会发生一些意想不到的结果。
这并不能算作一个BUG,因为file_get_contents函数读取文件的时候,是按二进制来读取的,读取到的内容是包含BOM的,而用户操作的时候,想当然的以为读取到的内容是不包含BOM的文本内容(如用记事本打开后看到的内容),因为BOM在编辑软件中是不可见的,只有在十六进制模式下才可以看见,问题也就出在这,实际上是由于“操作不统一”造成的。
当对UTF-8编码的文件进行操作时,如果要把读取的内容当作文本内容来处理,最好先对BOM进行一些处理,这个问题在PHP6中得到了解决(可以设置文本/二进制读取模式),有兴趣的朋友可以自己查找PHP6的手册。
一个较简单的解决方法:
1
2
3
4
5
6
7
8
|
<?php
$dataStr
=
file_get_contents
(
'test.txt'
);
if
(preg_match(
'/^\xEF\xBB\xBF/'
,
$dataStr
))
{
$dataStr
=
substr
(
$dataStr
,3);
}
//对$dataStr进行操作
?>
|
什么是BOM?
BOM是Byte Order Mark的缩写,即字节顺序标记,它是插入到UTF-8,UTF-16或UTF-32编码的Unicode文件开头的特殊标记,用来标识Unicode文件的编码类型。
几种编码对应的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)
对于UTF-8编码的文件而言,BOM标记是可有可无的,Windows自带的记事本文件在保存为UTF-8编码时,会自动加上BOM,现在一些编辑软件,可以在保存为UTF-8编码时可以选择是否带BOM保存。
对于PHP文件,在使用UTF-8编码时,最好都不要BOM保存。因为当你使用include/require/include_once/require_once这些函数去包含一个带BOM的文件时,你得到的网页,在某些兼容性不是很好的浏览器下,你会发现你的网页的实际显示效果跟预期的有细微的差别。
2、检测文件是否有bom头
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
<?php
//此文件用于快速测试UTF8编码的文件是不是加了BOM,并可自动移除
//By Bob Shen
$basedir
=
"."
;
//修改此行为需要检测的目录,点表示当前目录
$auto
= 1;
//是否自动移除发现的BOM信息。1为是,0为否。
// 以下不用改动
if
(
$dh
= opendir(
$basedir
)) {
while
((
$file
= readdir(
$dh
)) !== false) {
if
(
$file
!=
'.'
&&
$file
!=
'..'
&& !
is_dir
(
$basedir
.
"/"
.
$file
))
echo
"filename: $file "
.checkBOM(
"$basedir/$file"
).
" <br>"
;
}
closedir
(
$dh
);
}
function
checkBOM (
$filename
) {
global
$auto
;
$contents
=
file_get_contents
(
$filename
);
$charset
[1] =
substr
(
$contents
, 0, 1);
$charset
[2] =
substr
(
$contents
, 1, 1);
$charset
[3] =
substr
(
$contents
, 2, 1);
if
(ord(
$charset
[1]) == 239 && ord(
$charset
[2]) == 187 && ord(
$charset
[3]) == 191) {
if
(
$auto
== 1) {
$rest
=
substr
(
$contents
, 3);
rewrite(
$filename
,
$rest
);
return
(
"<font color=red>BOM found, automatically removed.</font>"
);
}
else
{
return
(
"<font color=red>BOM found.</font>"
);
}
}
else
return
(
"BOM Not Found."
);
}
function
rewrite (
$filename
,
$data
) {
$filenum
=
fopen
(
$filename
,
"w"
);
flock
(
$filenum
, LOCK_EX);
fwrite(
$filenum
,
$data
);
fclose(
$filenum
);
|