Python 3中的json.dumps,会将中文转换为unicode编码后保存

2021-01-18 11:47:34 浏览数 (1)

参考链接: Python-json 7:Unicode和非ASCII字符编码为JSON

先把这次踩坑的结论放在最前面 

1. Python 3已经将unicode作为默认编码 

2. Python 3中的json在做dumps操作时,会将中文转换成unicode编码,并以16进制方式存储,再做逆向操作时,会将unicode编码转换回中文 

这就解释了,为什么json.dumps操作后,得到的字符串是uXXXX。 

谢谢@benpaodexiaowoniu。 

在python3中,ensure_ascii=False就解决了问题 

------------------分割线以下部分纯属扯淡,没时间的现在可以关闭页面了------------------------- 

如果不知道上面两点,加之python之前对编码处理的不好名声,就会陷入一个问题深坑中。 

读到这里,如果你赶时间,就可以不用往下看了。 

爆出问题的代码如下: 

@app.route("/<desc>/<amount>") 

def addSimpleRecord(desc, amount): 

    ..... 

        file.write(json.dumps(buildJson(desc, amount))); 

    .... 

整个程序运行正常,但当我打开文件看到保存的中文数据变成了uXXXX时,头都大了。 

经过了各种尝试,我发现网上对python3中的编码问题进行了如下归纳 

uXXXX是unicode 16进制编码的表现形式在文件的第一行加上# -*- coding: utf-8 -*-对字符串对象进行str.decode('...').encode('...')对字符串对象进行操作:str.encode('utf-8').decode('unicode_escape')

关于第一条,print("你" == "u4f60")得到的结果是True 

关于第二条,那是python2的故事,在python3中默认的文件编码就是utf-8。因此,在保存python 3的脚本时,请务必保存为utf-8。 

关于第三条,那也是python2的故事,在python3中,字符串默认采用unicode编码。 

关于第四条,最初我是参考  python3 把u开头的unicode转中文,把str形态的unicode转中文 ,发现不能重现,后来当我用\uXXXX时,就重现了这篇文章中描述的问题,因为在python字符串中起转义作用(我居然给忘记了)。 

为什么json.dumps处理过后的中文就变成了uXXXX呢?原因在这里py_encode_basestring_ascii 

ESCAPE_ASCII = re.compile(r'([\"]|[^ -~])')

HAS_UTF8 = re.compile(b'[x80-xff]')

ESCAPE_DCT = {

    '\': '\\',

    '"': '\"',

    'b': '\b',

    'f': '\f',

    'n': '\n',

    'r': '\r',

    't': '\t',

}

def py_encode_basestring_ascii(s):

    """Return an ASCII-only JSON representation of a Python string

    """

    def replace(match):

        s = match.group(0)

        try:

            return ESCAPE_DCT[s]

        except KeyError:

            n = ord(s)

            if n < 0x10000:

                return '\u{0:04x}'.format(n)

                #return '\ux' % (n,)

            else:

                # surrogate pair

                n -= 0x10000

                s1 = 0xd800 | ((n >> 10) & 0x3ff)

                s2 = 0xdc00 | (n & 0x3ff)

                return '\u{0:04x}\u{1:04x}'.format(s1, s2)

    return '"' ESCAPE_ASCII.sub(replace, s) '"'

关键的地方就在: 

return '\u{0:04x}'.format(n) 

其中{0:04x}表示取第一个参数,并取长度为4的16进制小写数字。 

json.dumps的参数中有一个参数ensure_ascii,其默认值为True。我想这么做的目的可能是为了跨平台的通用性。 

到此为止,python 3的编码问题总算是告一段落,如有问题,欢迎交流。

0 人点赞