项目中我们经常需要获取某个服务的JSON数据。如果响应返回的是JSON格式的数据,客户端通过JSON工具可正常解析。但如果碰到值里面有中文的,特别是返回的格式是类似“{"name": "\u5927\u7231\u4e2d\u56fd"}”处理起来会比较麻烦。本文将从编码解码原理出发详细介绍该类问题的解决办法。
涉及编解码可以参考文章:编程开发中的的字符编码与解码-原理篇_hongdi的博客-CSDN博客
涉及JSON数据处理可以参考:达观RPA实战-JSON数据解析_hongdi的博客-CSDN博客
编解码的简要总结如下:
编码(encode):将Unicode字符串(中的代码点)转换特定字符编码对应的字节串的过程和规则
解码(decode):将特定字符编码的字节串转换为对应的Unicode字符串(中的代码点)的过程和规则
因为达观RPA的代码主要是Python编写的,下面就以几个示例简要说明下python中的中文汉字的编解码过程。开始之前先介绍几个前提:
先看下图:
该图引用自https://www.cnblogs.com/zhangqigao/p/6496172.html
1.源代码文件的保存。因为文本文件,包括源码,保存时都是以特定编码的字节形式存放的,如源代码文件的字符编码是一般是由编辑器指定的,比如我们使用Pycharm来编写Python程序时会指定工程编码和文件编码为UTF-8,Python代码保存到磁盘时会被转换为UTF-8编码对应的字节(encode过程)后写入磁盘。
(2)源代码文件的执行
Python程序执行时,将Python程序文件加载到内存时所使用的字符编码,会影响python解释器的识别与执行。在Python 2中,Python解释器在读取到中文字符的字节码尝试解码操作时,会先查看当前代码文件头部是否指明当前代码文件中保存的字节码对应的字符编码是。如果没有则使用默认字符编码"ASCII"进行解码,此时如果有中文Python解释器就会报错。这是因为Python 2解释器执程序时试图从ASCII编码表中查找中文字符对应的二进制序列,但是发现找不到。
在Python 3的解释器默认使用UTF-8编码,它本身是可以对中文字符进行编码和解码的,所以即便不指定字符编码也能正常运行。
这里要注意:文件的编码格式和解释器的编码是不一样的两个概念,如果两个编码不一致,代码运行时涉及中文的会乱码。
因此,为了兼容Pyhon2和python3,可以通过在文件的开头添加:# -*- coding:utf-8 -*-
(2)执行时字符串的处理。
可以通过字符串中的utf-8、gbk、unicode编码的python实现看看编码前后的区别
- a='大爱中国'
- print('===============编码前的字符串a=',a)
- print(type(a),len(a))
-
- b=a.encode('gbk')
- print('编码为gbk后的字符串b=',b)
- print(type(b),len(b))
-
- c=a.encode('unicode_escape')
- print('编码为unicode后的字符串c=',c)
- print(type(c),len(c))
-
- d=a.encode('utf-8')
- print('编码为utf8后的字符串d=',d)
- print(type(d),len(d))
输出:
===============编码前的字符串a= 大爱中国
编码为gbk后的字符串b= b'\xb4\xf3\xb0\xae\xd6\xd0\xb9\xfa'
编码为unicode后的字符串c= b'\\u5927\\u7231\\u4e2d\\u56fd'
编码为utf8后的字符串d= b'\xe5\xa4\xa7\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd'
可以看出定义的字符串变量,其类型为str,而编码后都转换为bytes,而且转换后的编码不一样
在看看另外一个代码:
- e = '\u5927\u7231\u4e2d\u56fd'
- print('===============编码前的字符串e=',e)
- print(type(e),len(e))
-
- h=e.encode('gbk')
- print('编码为gbk后的字符串h=',h)
- print(type(h),len(h))
-
-
- g=e.encode('unicode_escape')
- print('编码为unicode后的字符串g=',g)
- print(type(g),len(g))
-
- f=e.encode('utf-8')
- print('编码为utf8后的字符串f=',f)
- print(type(f),len(f))
输出:
===============编码前的字符串e= 大爱中国
编码为gbk后的字符串h= b'\xb4\xf3\xb0\xae\xd6\xd0\xb9\xfa'
编码为unicode后的字符串g= b'\\u5927\\u7231\\u4e2d\\u56fd'
编码为utf8后的字符串f= b'\xe5\xa4\xa7\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd'
可以发现两端代码虽然变量定义不一样,但是输出一样。第二段代码定义的字符串是用unicode编码给出的,解释器在处理时通过\u判断该字符是unicode编码,在内设字符表中可以找到对应的中文字符,进行了展示。
与python2不同,python3中改进了字符串支持(python2代码中定义的字符串变量其实是字节码),不仅仅是更改了默认编码,而是重新进行了字符串的实现,即python3代码中定义的字符串就是字符串,而且是unicode的字符串。
问题来了,为什么e = '\u5927\u7231\u4e2d\u56fd'与e=’大爱中国‘结果是一样的呢?
这个就跟之前提到的python3解释器有关,一般默认解释器是unicode,因此不需要先解码,可以直接编码成新的字符编码。所以 '\u5927\u7231\u4e2d\u56fd'与’大爱中国‘其实是同一个字符串。
如果第二段代码变量定义成:e = '\\u5927\\u7231\\u4e2d\\u56fd'
输出就完全不一样了,多了一个斜杆,\\u没有发生转义,变量当成了字符串处理。
===============编码前的字符串e= \u5927\u7231\u4e2d\u56fd
编码为gbk后的字符串h= b'\\u5927\\u7231\\u4e2d\\u56fd'
编码为unicode后的字符串g= b'\\\\u5927\\\\u7231\\\\u4e2d\\\\u56fd'
编码为utf8后的字符串f= b'\\u5927\\u7231\\u4e2d\\u56fd'
接着我们把变量e的定义改成编码后的utf8:
e = '\xe5\xa4\xa7\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd'
会发现输出乱码了,因为解释器不知道\x需要转义成什么,后续gbk\utf8\unicode都会报错。
如果在e前面加b,如:e = b'\xe5\xa4\xa7\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd'
则可以指定该变量是一个字节码,用e.decode('utf-8')进行解码后,可以在字符表中找到对应的字符。如果是解码成gbk,则会提示如下,找不对应字符:
UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 8: illegal multibyte sequence
如果e的初始化是gbk字节码即e=b'\xb4\xf3\xb0\xae\xd6\xd0\xb9\xfa'
则通过e.decode('gbk')可以进行解码操作,对于utf8\unicode解码就会报错。
上面介绍了python编解码的相关知识,下面结合实际案例,介绍下达观RPA的编解码控件的使用。
假设我们通过“发送http/https请求”和“解析http/https响应”控件获取到了一个JSON数据如下:
{"data":{"name": " \u5927\u7231\u4e2d\u56fd"}}
name值是后台经过unicode编码并转成字符串后的字符串,所以会出现\u。
案例中我们将上述的json串赋值给字符串变量,然后通过编码为utf8再解码成utf8,RPA编辑如下:
运行输出的结果如下:
[2023-09-13 11:38:11 DARPA INFO] 主流程.py:71: [打印日志-json串] {"data":{"name": " \u5927\u7231\u4e2d\u56fd"}}
[2023-09-13 11:38:11 DARPA INFO] 主流程.py:102: [打印日志-字典] {'data': {'name': ' 大爱中国'}}
[2023-09-13 11:38:11 DARPA INFO] 主流程.py:132: [打印日志-data] {'name': ' 大爱中国'}
[2023-09-13 11:38:11 DARPA INFO] 主流程.py:162: [打印日志-name的值] 大爱中国
原始的type和len
[2023-09-13 11:38:11 DARPA INFO] 主流程.py:211: [打印日志-编码后] 大爱中国
编码后的的type和len
[2023-09-13 11:38:11 DARPA INFO] 主流程.py:259: [打印日志-解码后] 大爱中国
解码后的type和len
-- 程序运行结束 --