很多语言或协议选择使用 ASCII 字符 “\”(backslash,0x5c) 作为字符串的转义符,包括 JSON 中的字符串。一般来说,使用 Python 中的 JSON 模块编码英文,不会存在转义符的问题。但如果使用 JSON 模块编解码中文,就可能面临着中文字符包含转义符带来的 bug。本篇文章给出了一个 badcase。
中文解码错误
测试用例文件里面包含繁体的“運動”二字,使用 GB18030 编码。使用 json 解码的错误如下:
$ cat decode.dat {"a":"運動"} $ python >>> import json >>> fp=open('decode.dat', 'r') >>> json.load(fp, encoding='gb18030') Traceback (most recent call last): File "", line 1, in File "/home/yangwb/local/lib/python2.7/json/__init__.py", line 278, in load **kw) File "/home/yangwb/local/lib/python2.7/json/__init__.py", line 339, in loads return cls(encoding=encoding, **kw).decode(s) File "/home/yangwb/local/lib/python2.7/json/decoder.py", line 360, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/home/yangwb/local/lib/python2.7/json/decoder.py", line 376, in raw_decode obj, end = self.scan_once(s, idx) UnicodeDecodeError: 'gb18030' codec can't decode byte 0xdf in position 0: incomplete multibyte sequence
发生这个问题的原因,就存在于“運”字的编码之中。“運”的 GB18030 编码是 0xdf5c,由于第二个字符与转义符 “\” 编码相同,所以剩下的这个 0xdf 就被认为是一个 incomplete multibyte sequence。
我本来认为,既然已经提供了编码,json 模块就能够区分汉字与转义符(所以我觉得这应该是 json 的一个 bug)。但从实验来看,并非如此。对于一些不需提供字符编码的 JSON 解码器来说,我们倒可以用一种比较 tricky 的方法绕过上面这个问题,即在“運”字后面加一个额外的转义符:
{"a":"運\動"}
遗憾的是,这种方法对 Python 的 json 模块不适用。我仍不知道该如何解决这个解码问题。
中文编码——没错误!
对于相同的 case,Python 倒是能够编码成功:
$ cat in.dat 運動 $ python >>> import json >>> in_str = open('in.dat', 'r').read() >>> out_f = open('out.dat', 'w', 0) >>> dump_str = json.dumps({'a': in_str}, ensure_ascii=False, encoding='gb18030') >>> out_f.write(dump_str.encode('gb18030')) $ cat out.dat {"a": "運動"}
所以这件事情就把我给搞糊涂了,Python 的 json 模块不能解码自己编码的 json 串。所以我觉得这可能是一个 bug,或者至少是 2.7.1 版本的 bug。
PS: 要仔细看文档
20120516:经网友 TreapDB 提醒,加载字符串时自己做 Unicode 转换,貌似能够解决这个问题。
$ cat decode.dat {"a":"運動"} $ python >>> import json >>> in_str = open('decode.dat', 'r').read().decode('gb18030') >>> json.loads(in_str)
回头仔细看了一下 json 的文档,其中有这么一段:
Encodings that are not ASCII based (such as UCS-2) are not allowed, and should be wrapped with codecs.getreader(encoding)(fp), or simply decoded to a unicode object and passed to loads().
已经注明了 encoding 不支持非 ASCII-based 编码的参数,所以应该使用 getreader 进行转码,而不是让 json 模块去转码。看来是我没读懂文档,大惊小怪了,回家面壁去!
>>> json.load(codecs.getreader('gb18030')(fp))
原来是 Big5 的“許蓋功”问题复活了。
哈哈,看来不止我一个人被类似问题困扰过。
JSON rfc说了
JSON text SHALL be encoded in Unicode. The default encoding is UTF-8.
http://www.ietf.org/rfc/rfc4627.txt?number=4627
所以python的实现没有处理其它非Unicode编码字符集,我猜的哈
多谢提供这个信息,这对于规范JSON格式很关键。
此问题曾经遇到过
在python脚本开头 加上这个就可以了, 这样 就不需要 加encode之类的reload(sys)sys.setdefaultencoding( "utf-8" )
我现在遇到了更严重的问题,就是经过unicode转换后依然不能loads,这很明显是python的bug啊