程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Python >> Python JSON模塊解碼中文的BUG

Python JSON模塊解碼中文的BUG

編輯:Python

很多語言或協議選擇使用 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))

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved