热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

包含utf8字符的pickle转json的大坑处理过程

背景:希望将pickle转换为json,由于pickle里有utf8的字符,因此转换失败。转换代码如下:Convertap

背景:希望将pickle转换为json,由于pickle里有utf8的字符,因此转换失败。

转换代码如下:

'''
Convert a pkl file into json file
'''
import sys
import os
import pickle
import jsondef convert_dict_to_json(file_path):with open(file_path, 'rb') as fpkl, open('%s.json' % file_path, 'w') as fjson:data = pickle.load(fpkl)json.dump(data, fjson, ensure_ascii=False, sort_keys=True, indent=4)def main():if sys.argv[1] and os.path.isfile(sys.argv[1]):file_path = sys.argv[1]print("Processing %s ..." % file_path)convert_dict_to_json(file_path)else:print("Usage: %s abs_file_path" % (__file__))if __name__ == '__main__':main()

修正后的代码:因为pickle自身是latin编码,需要转换为utf8再给json编码。

# -*- coding: utf-8 -*-'''
Convert a pkl file into json file
'''
import sys
import os
import pickle
import json
import iodef convert_dict_to_json(file_path):out_file = "vocab_dict.json"with open(file_path, 'rb') as fpkl, io.open(out_file, 'w', encoding='utf8') as fjson:data = pickle.load(fpkl)out_data = data["valid_chars"]out_data["__MAX_DOC_LEN__"] = data["max_len"]out_data["__VOLCAB_SIZE__"] = data["volcab_size"]out = {}for k,v in out_data.items():#print k.decode("latin1").encode("utf8")out[k.decode("latin1").encode("utf8")] = vdata = json.dumps(out, ensure_ascii=False, encoding="utf8", sort_keys=True, indent=4)fjson.write(data)def main():if sys.argv[1] and os.path.isfile(sys.argv[1]):file_path = sys.argv[1]print("Processing %s ..." % file_path)convert_dict_to_json(file_path)else:print("Usage: %s abs_file_path" % (__file__))

 

尤其注意:

python2的json是无法处理,包含utf8和unicode两种编码的!比如仅仅包含unicode的可以处理很好:

# -*- coding: utf-8 -*-import json
from codecs import openo = { 'text': u'木村' }with open('foo.json', 'w', encoding= 'utf-8') as fp:json.dump(o, fp, ensure_ascii= False)with open('foo.json', 'r', encoding= 'utf-8') as fp:
print json.load(fp)['text'].encode('utf-8')

但是:

o = { 'text': u'木村', 'text2': '再加一个utf8的python json就熄火了,会有编码错误异常。。。' }

具体见下文:

python2 json的大坑

介绍一下背景

最近项目中有一个接口,是通过redis队列做的。我将对方需要的数据通过json 字符串的形式,push到redis list队列中,对方监听并消费(题外话, 我对这种形式的交互有点看法吧,双方既然是接口,但是很难保证格式的统一,比使用rpc框架强验证风险大的多)。

由于对端也是用python做的消费者,所以也是相安无事。随着一个需求的变更,我在自己调试pop的数据发现,我写的json字符串是,酱事儿的:

1
{"title": "\\u6211\\u7231\\u5317\\u4eac\\u5929\\u5b89\\u95e8"}

当时我就懵逼了,这是什么鬼…
很显然这个是unicode字符串嘛,但是我明明就编码成了UTF8啦,怎么最后是这个鬼样子,更奇怪的是对方能正确解码吗?这明明是四不像啊。

我试着自己重现了整个过程。
首先,我将utf8形式的字符串 我爱北京天安门 dumps成json:

1
2
In [10]: json.dumps({"title":"我爱北京天安门"})
Out[10]: '{"title": "\\u6211\\u7231\\u5317\\u4eac\\u5929\\u5b89\\u95e8"}'

果然,确实变成了这个样子,看来是json库搞得鬼。
先不管他,看看这个结果load出来什么样子:

1
2
In [9]: json.loads('{"title": "\\u6211\\u7231\\u5317\\u4eac\\u5929\\u5b89\\u95e8"}')
Out[9]: {u'title': u'\u6211\u7231\u5317\u4eac\u5929\u5b89\u95e8'}

确实load没问题,但是可以看到,最后的结果和我当时dumps的出入蛮大的,由原来的utf8 str形式,变为了unicode形式,就连字典的key也都是unicode了。

好吧,所以现在就有了两个问题。

  • 为什么utf8字符串, json dumps后不是原来形式?
  • 为什么loads回来的数据全是unicode形式?

为什么utf8字符串, json dumps后不是原来形式?

看下官方文档:
python encode
json.dumps方法做的就是将python数据格式按照上图的映射方式转换为json格式。Python str和unicode都可以转换成json 的string形式,我们知道str和unicode差别很大啊,如果一个python字典中,同时有str和unicode的时候,json dump怎么处理呢?试一下:

1
2
In [12]: json.dumps({"title_str":"我爱北京天安门", "title_unicode":u"我爱北京天安门"})
Out[12]: '{"title_unicode": "\\u6211\\u7231\\u5317\\u4eac\\u5929\\u5b89\\u95e8", "title_str": "\\u6211\\u7231\\u5317\\u4eac\\u5929\\u5b89\\u95e8"}'

没有异常,并且都是最后按照unicode的方式统一处理的。看来python是先将str decode为unicode,然后再用unicode进行编码的。

这样本来无可厚非,自己统一好编码格式就行了,loads的时候按照编码的方式,反过来解码。但是问题是,和我们进行交互的人未必也用的python啊,当他用其他的语言对json解码的时候,还原回来就是一堆乱码了,我们能不能让json库,确实编码成utf8形式呢?

官方文档如是说:

If ensure_ascii is True (the default), all non-ASCII characters in the output are escaped with \uXXXX sequences, and the results are str instances consisting of ASCII characters only. If ensure_ascii is False, a result may be a unicode instance. This usually happens if the input contains unicode strings or the encoding parameter is used.

看来是 ensure_ascii 参数为 True 的时候,确保了所有非ASCII字符都转义成 \uXXXX 的ASCII序列。
如果我们设置为False,就可以还原本来面目了吗?试试:

1
2
In [14]: json.dumps({"title_str":"我爱北京天安门"}, ensure_ascii=False)
Out[14]: '{"title_str": "\xe6\x88\x91\xe7\x88\xb1\xe5\x8c\x97\xe4\xba\xac\xe5\xa4\xa9\xe5\xae\x89\xe9\x97\xa8"}'

果然哦,我们干脆看看python json库源码怎么实现的吧, 主要就是下列这个判断

1
2
3
4
200 if self.ensure_ascii:
201 return encode_basestring_ascii(o) # 先将字符串根据encoding参数的编码统一转化为unicode,然后连接字符串
202 else:
203 return encode_basestring(o) # 直接连接字符串

既然ensure_ascii = False时, 没有做类型的转换,所以我们原来是什么,编码后就是什么。但这带来了以下副作用:

  • 如果我们要转换的python数据类型,如果既包含str又包含unicode,在连接字符串的时候肯定会抛出编码异常

    1
    2
    In [13]: json.dumps({"title_str":"我爱北京天安门", "title_unicode":u"我爱北京天安门"}, ensure_ascii=False)
    "UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 1: ordinal not in range(128)"
  • 如果全部都是unicode进行字符串连接,返回值也是unicode

    1
    2
    In [3]: json.dumps({"title_str":u"我爱北京天安门", "title":u"我爱世界"}, ensure_ascii=False)
    Out[3]: u'{"title": "\u6211\u7231\u4e16\u754c", "title_str": "\u6211\u7231\u5317\u4eac\u5929\u5b89\u95e8"}'
  • 如果全部都是str进行字符串连接,返回值也是str

    1
    2
    In [2]: json.dumps({"title_str":"我爱北京天安门", "title":"我爱世界"}, ensure_ascii=False)
    Out[2]: '{"title": "\xe6\x88\x91\xe7\x88\xb1\xe4\xb8\x96\xe7\x95\x8c", "title_str": "\xe6\x88\x91\xe7\x88\xb1\xe5\x8c\x97\xe4\xba\xac\xe5\xa4\xa9\xe5\xae\x89\xe9\x97\xa8"}'

为了能将json字符串通用的和其他语言交换,我们不得不保证,原始python数据类型必须是统一的。要么全是UTF8的str类型,要么全部是unicode,最后在encode为utf8, 否则就会有异常 这个也是动态类型要付出的代价吧。

为什么loads回来的数据全是unicode形式?

看下官方文档:
python decode
与dumps相反, json.loads 方法做的就是将json数据格式按照上图的映射方式转换为python类型。我们可以看json string 转换回来只有一种格式,那就是unicode,这样就能解释我们看到的现象了,就连dict key都是unicode的。

好麻烦啊,怎么根本的解决这个问题呢?

答: 使用python3


转载于:https://www.cnblogs.com/bonelee/p/8472522.html


推荐阅读
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • x86 linux的进程调度,x86体系结构下Linux2.6.26的进程调度和切换
    进程调度相关数据结构task_structtask_struct是进程在内核中对应的数据结构,它标识了进程的状态等各项信息。其中有一项thread_struct结构的 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 006_Redis的List数据类型
    1.List类型是一个链表结构的集合,主要功能有push,pop,获取元素等。List类型是一个双端链表的结构,我们可以通过相关操作进行集合的头部或者尾部添加删除元素,List的设 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • C++ STL复习(13)容器适配器
    STL提供了3种容器适配器,分别为stack栈适配器、queue队列适配器以及priority_queue优先权队列适配器。不同场景下,由于不同的序列式 ... [详细]
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 广度优先遍历(BFS)算法的概述、代码实现和应用
    本文介绍了广度优先遍历(BFS)算法的概述、邻接矩阵和邻接表的代码实现,并讨论了BFS在求解最短路径或最短步数问题上的应用。以LeetCode中的934.最短的桥为例,详细阐述了BFS的具体思路和代码实现。最后,推荐了一些相关的BFS算法题目供大家练习。 ... [详细]
  • angular.element使用方法及总结
    2019独角兽企业重金招聘Python工程师标准在线查询:http:each.sinaapp.comangularapielement.html使用方法 ... [详细]
author-avatar
wangtao
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有