Python中的Unicode转义序列

大家好,我是game1024,最近编写程序的时候经常碰到乱码的问题,中文死活显示不出来,以往都是直接使用encode,decode的一顿转换,最后虽然也解决了问题,但是并不明白其中的原理。对Unicode,UTF-8、GBK的概念也很模糊

最近又遇到了类似的问题,但是发现encode,decode大法不好使了,所以想彻底的弄清楚其中的原理

问题背景

问题背景是我在使用json进行序列化的时候,数据里面中文被转换成了\u5546\u54c1\u540d\u79f0这种格式的东东,一开始我以为是又碰到乱码问题了,但是在网上找了很多方法encode('utf-8') decode('utf-8')都不好使。

最后是在google上搜索,了解到json.dumps有一个ensure_ascii的参数,将这个参数设置成False就能解决

python 复制代码
data = json.dumps({
    'data':{
        'items': [
                     {
                         'id': '00000000',
                         'name': '商品名称',
                         'L1_category_name': '一级类目名称',
                         'L2_category_name': '二级类目名称',
                         'L3_category_name': '三级类目名称',
                         'ext_map': {
                             'score': 0.9123,
                             'dumps': 'search.xxxx.yyyy.zzzz'
                         }
                     }
                 ]*20
    },
    'traceId': '00000000000000000',
    'code':200
}, ensure_ascii=False)

print(data)

Unicode 转义序列

问题是解决了,可是还不够,我还想知道ensure_ascii参数到底做了什么,于是我问了一下豆包大模型,得到的回复是这样的

"json.dumps() 是 Python json 模块中的一个函数,可用于将 Python 对象序列化为 JSON 格式的字符串。当设置 ensure_ascii=True(默认值)时,非 ASCII 字符会被转换为 Unicode 转义序列。"

听下来的意思是,对于中文等一类的字符,当开启ensure_ascii=True,会把它转换成'\uxxxx'的格式

那么什么是unicode转义序列呢,带着问题我继续问大模型

大概意思就是\u后面那串xxxx的数字代表的是字符对应的unicode码值(十六进制)

带着好奇,我想做一下实验,比如"我"这个字符,经过unicode转义序列会变成什么,究竟后面对应的数字是不是unicode的码值

python 复制代码
print(json.dumps("我", ensure_ascii=True))

# 输出
"\u6211"

接着我去查了一下文档,在python里面可以通过ord()函数来获取一个字符的unicode码值

python 复制代码
print(ord('我'))

# 输出
25105

哎,怎么好像不太对,25105和6211不相等啊,等等!!!ord是返回的是十进制表达形式,\uxxxx是十六进制的表达形式,是不能直接拿来比较的,需要先把十进制的数转成十六进制的表达形式

python 复制代码
print(hex(ord('我')))

# 输出
0x6211

可以看到,确实"我"这个字符的转义序列的数字,和十六进制的unicode码值是可以对应上的

ensure_ascii的背后

我通过json.dumps("我", ensure_ascii=True)确实得到了"我"的unicode转义序列,不过我还想知道ensure_ascii这个参数背后到底做了什么,它是怎么把"我" => "\u6211"

带着问题我继续问大模型,怎么将一个字符变换成unicode转义序列,得到的回答是可以利用str.encode("unicode_escape")。

python 复制代码
# 得到'我'经过unicode_escape转义编码后的字节数组
bs = '我'.encode("unicode_escape")
print(bs)
# 输出
b'\\u6211'

可以看到有两个\\这是因为python解释器想要表示\这个字符的时候,需要两个\\

我们可以通过另一种方式,更直观地得到字节数组里面有什么

python 复制代码
bs = '我'.encode("unicode_escape")
for byte in bs:
    # hex:byte对应的十六进制表达形式
    # chr: byte对应的字符
    print(hex(byte), chr(byte))

# 输出
0x5c \
0x75 u
0x36 6
0x32 2
0x31 1
0x31 1

可以看到里面就是'\u6211'这个字符串对应的unicode码值(对于英文字符来说,unicode码值和ascii码值是一样的)

如果我想要得到'\u6211'这个字符串,只需要将字节数组decode('ascii')就行

python 复制代码
bs = '我'.encode("unicode_escape")
print(bs.decode('ascii'))

# 输出
\u6211

结论

所以实际上ensure_ascii=True背后做的事情,与下面的代码等价

python 复制代码
"a json string".encode("unicode_escape").decode("ascii")

如果我想要把一个unicode转义字符串,转换成正常显示的中文可以这样操作

python 复制代码
literal = r'\u6211'.encode("ascii").decode("unicode_escape")
print(literal)

# 输出
好

# 因为unicode转义序列中的所有字符都是0~127码值的字符
# 所以每个字符的utf-8编码于ascii编码是一样的
# 下面这段代码依然可以得到正确结果
literal = r'\u6211'.encode('utf-8').decode('unicode_escape")
print(literal)

# 输出
好

附录

python 复制代码
bin() # 获取一个整型数字的二进制形式
hex() # 获取一个整型数字的十六进制形式
ord() # 获取一个字符的unicode码值
chr() # 获取一个整型数字对应的字符

'我'.encode('utf-8')          # 获取字符串经过utf-8编码后的字节数组
'我'.encode('unicode_escape') # 获取字符串经过unicode_escape编码后的字节数组

r'\u6211'.encode('ascii').decode('unicode_escape') # 获取'\u6211'转义序列对应的字符串
相关推荐
AronTing5 分钟前
09-RocketMQ 深度解析:从原理到实战,构建可靠消息驱动微服务
后端·面试·架构
方块海绵6 分钟前
RabbitMQ总结
后端
星辰大海的精灵7 分钟前
Python 中利用算法优化性能的方法
后端·python
雷渊8 分钟前
深度分析Scroll API(滚动搜索)方案
后端
AronTing9 分钟前
11-Spring Cloud OpenFeign 深度解析:从基础概念到对比实战
后端·spring cloud·架构
yifuweigan9 分钟前
J2Cache 实现多级缓存
后端
洛神灬殇12 分钟前
【Redis技术进阶之路】「原理分析系列开篇」探索事件驱动枚型与数据特久化原理实现(时间事件驱动执行控制)
redis·后端
Java中文社群14 分钟前
SpringAI版本更新:向量数据库不可用的解决方案!
java·人工智能·后端
日月星辰Ace15 分钟前
蓝绿部署
运维·后端
D龙源17 分钟前
VSCode-IoC和DI
后端·架构