一、

json.dumps(var,ensure_ascii=False)并不能解决中文乱码的问题

python 2.7版本

# -*- coding: utf-8 -*-
m = {'a' : '你好'}

print m
=>{'a': '\xe4\xbd\xa0\xe5\xa5\xbd'}

print json.dumps(m)
=>{"a": "\u4f60\u597d"}

print json.dumps(m,ensure_ascii=False)
=>{"a": "\xe4\xbd\xa0\xe5\xa5\xbd"}

print json.dumps(m,ensure_ascii=False).decode('utf8').encode('gb2312')
=>{"a": "你好"}

最近接触python,遇到这个问题,上网查资料,基本上都是说json.dumps(var,ensure_ascii=False)可以解决中文编码问题,然而并没有。

要解决中文编码,需要知道python2.7对字符串是怎么处理的:

  • 由于# -- coding: utf-8 -- 的作用,文件内容以utf-8编码,所以print m 
    输出的是utf-8编码后的结果{‘a’: ‘\xe4\xbd\xa0\xe5\xa5\xbd’}
  • json.dumps 序列化时对中文默认使用的ascii编码, print json.dumps(m)输出unicode编码的结果
  • print json.dumps(m,ensure_ascii=False)不使用的ascii编码,以gbk编码 
    ‘你好’ 用utf8编码是 %E4%BD%A0%E5%A5%BD 用gbk解码是 浣犲ソ
  • 字符串在Python内部的表示是unicode编码。
  • 因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。
  • decode的作用是将其他编码的字符串转换成unicode编码
    decode(’utf-8’)表示将utf-8编码的字符串转换成unicode编码。
  • encode的作用是将unicode编码转换成其他编码的字符串
    encode(‘gb2312’),表示将unicode编码的字符串转换成gb2312编码。
  • python3中没有这种问题,所以最简单的方法是引入__future__模块,把新版本的特性导入到当前版本
from __future__ import unicode_literals

print json.dumps(m,ensure_ascii=False)

=>{"a": "你好"}
在写入文件的时候出现了Python2.7的UnicodeEncodeError: ‘ascii’ codec can’t encode异常错误

大神的解决方法: 
不使用open打开文件,而使用codecs:
from __future__ import unicode_literals
import codecs
fp = codecs.open('output.txt', 'a+', 'utf-8')
fp.write(json.dumps(m,ensure_ascii=False))
fp.close()

二、

我们知道,python中的字符串分普通字符串和unicode字符串,一般从数据库中读取的字符串会自动被转换为unicode字符串

下面回到重点,使用json.dumps时,一般的用法为:

>>> obj={"name":"测试"}

>>> json.dumps(obj)
'{"name": "\\u6d4b\\u8bd5"}'

>>> print json.dumps(obj)
{"name": "\u6d4b\u8bd5"}

>>> json.dumps(obj).encode("utf-8") 
'{"name": "\\u6d4b\\u8bd5"}'

可以看到这里输出的字符串为普通字符串,但是里面的内容却是unicode字符串的内容,即使对结果进行encode("utf-8") ,因为这个字符串本身就已经编码过了,所有进行encode不会有变化

要想得到字符串的真实表示,需要用到参数ensure_ascii=False(默认为True):

>>> json.dumps(obj,ensure_ascii=False)  
'{"name": "\xe6\xb5\x8b\xe8\xaf\x95"}'

>>> print json.dumps(obj,ensure_ascii=False)
{"name": "测试"}

 

 坑:试试下面的用法(比如key是从数据库中读取的,则会以unicode字符串形式存在):

>>> key=u"name"
>>> obj={key:"测试"}   
>>> json.dumps(obj,ensure_ascii=False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.6/json/__init__.py", line 237, in dumps
    **kw).encode(obj)
  File "/usr/lib64/python2.6/json/encoder.py", line 368, in encode
    return ''.join(chunks)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 1: ordinal not in range(128)

 

个人认为:unicode编码和utf8编码混用,很容易引起这种错误!!!

 

这是因为key和value不能以混合普通字符串和unicode字符串的形式存在

改成下面则正常了(同时为普通字符串或同时为unicode字符串)

>>> key=u"name"
>>> obj={key:u"测试"}
>>> json.dumps(obj,ensure_ascii=False)
u'{"name": "\u6d4b\u8bd5"}'
>>> obj={key.encode("utf-8"):u"测试".encode("utf-8")}
>>> json.dumps(obj,ensure_ascii=False)               
'{"name": "\xe6\xb5\x8b\xe8\xaf\x95"}'

另外说说还有一个参数default

考虑下面的场景:

>>> class Data:
...     def __init__(self):
...         self.name = ""
...         self.detail = ""
...

>>> data=Data()
>>> data.name="名字"
>>> data.detail="细节"
>>> obj={"data":data}
>>> json.dumps(obj,ensure_ascii=False)

会报下面的异常:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.6/json/__init__.py", line 237, in dumps
    **kw).encode(obj)
  File "/usr/lib64/python2.6/json/encoder.py", line 367, in encode
    chunks = list(self.iterencode(o))
  File "/usr/lib64/python2.6/json/encoder.py", line 309, in _iterencode
    for chunk in self._iterencode_dict(o, markers):
  File "/usr/lib64/python2.6/json/encoder.py", line 275, in _iterencode_dict
    for chunk in self._iterencode(value, markers):
  File "/usr/lib64/python2.6/json/encoder.py", line 317, in _iterencode
    for chunk in self._iterencode_default(o, markers):
  File "/usr/lib64/python2.6/json/encoder.py", line 323, in _iterencode_default
    newobj = self.default(o)
  File "/usr/lib64/python2.6/json/encoder.py", line 344, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <__main__.Data instance at 0x11e87e8> is not JSON serializable

这是因为json.dumps不知道如何对Data对象进行序列化,需要定义一个函数,并赋给参数default:

>>> def convert_to_builtin_type(obj):
...     d = {}
...     d.update(obj.__dict__)
...     return d
...

>>> json.dumps(obj,ensure_ascii=False, default=convert_to_builtin_type)
'{"data": {"name": "\xe5\x90\x8d\xe5\xad\x97", "detail": "\xe7\xbb\x86\xe8\x8a\x82"}}'
>>> print json.dumps(obj,ensure_ascii=False, default=convert_to_builtin_type)
{"data": {"name": "名字", "detail": "细节"}}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 utf8中文:unicode中文