qpython能使用json吗l_[python] 详解Python在使用JSON时需要注意的编码问题
Python 中的字符编码
在 Python3 中, 字符 在内存中是使用 Unicode 存储的, 常规的字符使用 两个字节 表示, 一些很生僻的字符就需要 四个字节. 默认使用 Unicode 存储是什么意思呢, 那就是例子来解释一下, 在 Python Shell 中输入以下字符串 '\u4e2d\u6587', 观察其输出:
In [51]: '\u4e2d\u6587'
Out[51]: '中文'
输出的为 中文 两个字. 其实 \u4e2d 和 \u6587 分别表示 中 和 文 的 Unicode 编码(术语称为 码点)的 十六进制 表示, 在 Python3 中以 \u 开头的字符串被解析为 Unicode 字符, 然后通过其十六进制 码点 解析出具体的字符, 所以 中文 的内存表示即为 \u4e2d\u6587.
2. 获取字符 Unicode 码点
标准库提供了 ord 函数输出一个字符的 Unicode 码点, 使用 chr 函数将 码点 转换成 字符, 下面是示例:
In [54]: ord('中')
Out[54]: 20013
In [56]: chr(20013)
Out[56]: '中'
输出的 码点 是使用 十进制 表示的, 可以使用以下代码将整数格式化成十六进制字符串:
'{0:04x}'.format(20013)
使用 json.dumps
有了前面的铺垫, 就可以来说说 json.dumps 了. 下面以一个例子展开:
In [121]: json.dumps('中文', ensure_ascii=True)
Out[121]: '"\\u4e2d\\u6587"'
In [122]: json.dumps('中文', ensure_ascii=False)
Out[122]: '"中文"'
可以看到, 在 ensure_ascii 为 True 的情况下, 中文 被编码成了 Unicode 码, 为 False 才能正常显示, 但是这跟 ASCII 有什么关系呢? 来看一下官方文档对这个参数的解释:
如果 ensure_ascii 是 true (即默认值),输出保证将所有输入的非 ASCII 字符转义。如果 ensure_ascii 是 false,这些字符会原样输出。
现在稍微明白了, 在 ensure_ascii 为 True 的情况下, 如果字符串中存在 非ASCII 字符就将其转义, 根据结果可以知道这个字符被转义为 Unicode 码并格式化成了一个字符串, 注意 "\\u4e2d\\u6587" 与 "\u4e2d\\u6587" 是不同的, 前者是长度为 12 的字符串, 后者会被 Python 直接解析为 中文, 长度为 2. 这也就是我一开始出现的问题, 直接将转义的字符串在网络上传输可能会无法被识别. 比如 中文 被转义成 \\u4e2d\\u6587, 而服务器如果不知道它是被转义过的字符串, 那它就是一个长度为 12 的普通字符串, 肯定会识别出错. 而将 ensure_ascii 设为 False 就不会进行转义, 使用原始字符.
识别转义字符
如果服务器收到数据后发现是被转化过的, 那怎么识别呢? 其实被转义字符串与使用 unicode_escape 对字符串进行编码再使用 utf-8 进行解码的结果一致, 代码如下:
In [129]: msg
Out[129]: '中文'
In [130]: msg.encode('unicode_escape').decode('utf-8')
Out[130]: '\\u4e2d\\u6587'
所以识别只要反过来使用 utf-8 编码再使用 unicode_escape 解码就可以了.
转义是如何进行的
现在来看一下 json 到底是怎么对字符进行转义的. 在 json.dumps 源码中仔细调试的话会发现, 它调用的是 JSONEncoder.encode 方法, 而 encode 中的代码片段如下:
if self.ensure_ascii:
return encode_basestring_ascii(o)
else:
return encode_basestring(o)
它会根据 ensure_ascii 的值选择调用函数. 而 encode_basestring_ascii 的值是 (c_encode_basestring_ascii or py_encode_basestring_ascii), 也就是默认是用 C 实现的版本, 其次使用 Python 实现的版本, 既然有 Python 版本, 当然要看一下是怎么实现的, py_encode_basestring_ascii 可以直接使用 from json.encoder import py_encode_basestring_ascii 导入, 直接在其内部就可以调试. 下面是其源码:
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) # 输出一个字符的 Unicode 码点
if n < 0x10000:
return '\\u{0:04x}'.format(n)
#return '\\u%04x' % (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 可以看到它实际上是 正则替换 最后在前后添加 双引号. ESCAPE_ASCII 的定义如下:
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') # 匹配所有编码不在 40 ~ 176 的字符, 所以中文就会被匹配到,
其中 ([\\"] 用于匹配 \\ 和 ", 而 [^\ -~] 表示 \ -~ 取反(这里的反斜杠貌似是对空格进行转义, 我不是很理解, 不进行转义依旧可以匹配到), 在 ASCII 表里, 空格字符 对应十进制是 40, ~ 是 176, 这是所有的可打印字符, 取反就是所有编码不在 40 ~ 176 的字符, 所以中文就会被匹配到,
对于匹配到的字符, 会传入回调函数 replace 做转义. replace 函数中的 ESCAPE_DCT 为:
ESCAPE_DCT = {
'\\': '\\\\',
'"': '\\"',
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
}
会对常用字符进行转义, 如果失败就获取它的 Unicode 码点, 然后判断是否为小于 0x10000 即是否为 两字节 字符(两字节最大为0xFFFF) , 如果是就格式化为 Unicode 码, 如果不是就使用 四字节 表示.
为什么json.dumps处理过后的中文就变成了\uXXXX呢?原因在这里py_encode_basestring_ascii
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') # 匹配所有编码不在 40 ~ 176 的字符, 所以中文就会被匹配到,
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) # 输出一个字符的 Unicode 码点
if n < 0x10000: # 是否为 两字节 字符
return '\\u{0:04x}'.format(n) # 如果是就格式化为 Unicode 码
#return '\\u%04x' % (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) + '"'
参考:详解Python在使用JSON时需要注意的编码问题_python_脚本之家www.jb51.net
qpython能使用json吗l_[python] 详解Python在使用JSON时需要注意的编码问题相关推荐
- map函数的用法python,详解Python map函数及Python map()函数的用法
python map函数 map()函数 map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list ...
- python中字典和json的区别_详解python中的json和字典dict
定义 python中,json和dict非常类似,都是key-value的形式,而且json.dict也可以非常方便的通过dumps.loads互转.既然都是key-value格式,为啥还需要进行格式 ...
- python最早引入json的版本_详解Python在使用JSON时需要注意的编码问题
写这篇文章的缘由是我使用 reqeusts 库请求接口的时候, 直接使用请求参数里的 json 字段发送数据, 但是服务器无法识别我发送的数据, 排查了好久才知道 requests 内部是使用 jso ...
- 选择排序法python详解-Python选择排序算法(三)
优化选择排序算法,大致思路是每次循环分别找到最大值和最小值,放到列表的头部和尾部 代码如下: #! coding:utf8 import random def select_sort(arr): fo ...
- python 协程可以嵌套协程吗_Python | 详解Python中的协程,为什么说它的底层是生成器?...
今天是Python专题的第26篇文章,我们来聊聊Python当中的协程. 我们曾经在golang关于goroutine的文章当中简单介绍过协程的概念,我们再来简单review一下.协程又称为是微线程, ...
- python中文编码-python中文编码与json中文输出问题详解
前言 python2.x版本的字符编码有时让人很头疼,遇到问题,网上方法可以解决错误,但对原理还是一知半解,本文主要介绍 python 中字符串处理的原理,附带解决 json 文件输出时,显示中文而非 ...
- python3 json模块_详解python 3.6 安装json 模块(simplejson)
JSON 相关概念: 序列化(Serialization):将对象的状态信息转换为可以存储或可以通过网络传输的过程,传输的格式可以是JSON,XML等.反序列化就是从存储区域(JSON,XML)读取反 ...
- python json方法详解_详解python中的json的基本使用方法
在Python中使用json的时候,主要也就是使用json模块,json是以一种良好的格式来进行数据的交互,从而在很多时候,可以使用json数据格式作为程序之间的接口. #!/usr/bin/env ...
- scrapy爬虫储存到mysql_详解Python之Scrapy爬虫教程NBA球员数据存放到Mysql数据库
获取要爬取的URL 爬虫前期工作 用Pycharm打开项目开始写爬虫文件 字段文件items # Define here the models for your scraped items # # S ...
最新文章
- android fadingedge,Android:从滚动条中仅删除底部的FadingEdge效果
- 学习excel的使用技巧一空格替换为0
- 超融合架构下的数据中心
- Spring RESTFul Client – RestTemplate Example--转载
- shell训练营Day31
- 直播预告 | 基于多智能体交流游戏的零资源机器翻译
- 用了2年多快3年的老ASUS本子出了点小问题了
- 笨人可以学计算机吗,为什么有的笨人一旦开窍,其人生就像开了挂似的呢?
- i/o timeout , 希望你不要踩到这个net/http包的坑
- php面试题——Linux部分(高级部分)
- 设为首页 和 收藏本页
- 鼠标单击元素输出对应元素的索引号
- squid笔记下载_Squid下载-Squid鱿鱼笔记下载v3.4.9.5 安卓版-西西软件下载
- osgearth加载mapbox在线高程数据
- ROS的激光雷达、 加速度计、 陀螺仪传感器
- lol手游账号服务器,英雄联盟手游
- 第十四期公关大讲堂:公关稿件写作理念、方法与技巧.
- 电脑报价管理系统C语言,C语言笔记本电脑销售系统课设(附源码).doc
- char在c语言中的意思(char在c++中的意思)
- GitHub下载加速利器
热门文章
- 小数第n位java_蓝桥杯【历届试题 小数第n位】 java版 数论
- 虚拟运营商人工服务器,四大必想之事:倒闭、价格、网络
- MySQL如何查全部序列_Oracle查询所有序列
- redis哨兵主从不切换_Redis的三种模式:主从、哨兵、集群
- python twisted框架_Python 基于Twisted框架的文件夹网络传输源码
- 个人电脑 公司电脑 代理_你们想要打造的树莓派电脑,刚发布了官方版:性能更强大,只卖70美元...
- C语言之字符串探究(六):sprintf——把格式化的数据写入某个字符缓冲区
- linux open o_creat 失败,linux C代码 open函数参数:O_APPEND问题求助
- android 用户中心布局,android用户中心头像选择功能的方法实现-Go语言中文社区
- angularjsl路由_HTML5模式下的AngularJS路由404错误