您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

理解Python的编码问题

时间:2020-07-05 10:17:43  来源:  作者:

一、字符编码初探

字符编码其实就是将人类能识别的字符与计算机能识别的数字对应起来。ASCII(American Standard Code for Information Interchange)美国信息交换标准代码,是最早最通用的单字节编码标准。

ASCII单字节编码表示范围有限,是不能满足表示中文的,于是基于ASCII扩展,制定了GB2312标准(GB是国标的意思)。现在最常用的中文编码标准GBK又是GB2312的升级,能表示更多的字符。

计算机的发展和普及在各个国家和地区各有不同,各国也是制定了自己的编码标准,这些基于ASCII扩展而来,使用多字节表示字符的延伸编码方式称为 ANSI 编码。在简体中文windows系统中,ANSI编码代表GBK,而韩文系统中ANSI编码代表EUC-KR。

由于不同国家和地区编码标准不一致,也导致了它们之间存在复杂的编码转换,于是诞生了unicode编码方式,以提供统一的编码标准,所以unicode也叫万国码,其标准称呼应该是Universal Multiple-Octet Coded Character Set,简称UCS。而unicode又存在多种编码方式的实现,其中UTF-8是最常用的一种UTF(UCS Transfer Format)标准。

二、Python2/python3的默认编码

python2的默认编码是ascii,python3则是utf-8。可以通过如下方式获取:

python2 -c "import sys;print(sys.getdefaultencoding())"
python3 -c "import sys;print(sys.getdefaultencoding())"

可以在python文件开头设置默认编码,python3默认就使用了utf-8,所以不需要该编码声明。

# -*- coding: UTF-8 -*-
# coding=utf-8

python2虽然指定了编码,但还是不能很好地处理中文,在终端输出、文件读写、json处理等都难免遇到问题。要解决各种编码问题,需要明确当前编码是什么,python编码的相关特性,数据来源是什么编码,数据输出又是什么编码。

三、起点-python2打印输出中文

我们在python2文件开头设置了编码方式为utf-8,如果cmd终端字符编码不是utf-8,要正常打印输出中文,还 需要将字符串先解码,再编码成终端的编码格式输出 

比如,中文windows系统的cmd终端默认是gbk中文编码,chcp查看活动代码页编号是936,也就是gbk编码,要正常输出中文,字符串需要先解码,再编码成终端的gbk格式打印,如下。

# -*- coding: UTF-8 -*-
#python2
import sys
print("中文".decode('utf-8').encode(sys.stdout.encoding))  #文件开头已经指定默认编码为utf-8,但是终端是gbk,所以需要先decode('utf-8') 再 encode(sys.stdout.encoding)

四、特性初识-python2/python3的unicode类型

python2与python3在定义unicode类型时是通用的。

#定义unicode类型
u = u'中文'
u = u"中文"
u = u'''中文'''
u = u"""中文"""

我们知道unicode是通用的,所以无论使用python2还是python3执行如下代码时均不会出现乱码。

print(u"中文")
print(u'\u4e2d\u6587')#均输出中文

所以前面python2 打印输出 的问题其实可以简写。

print("中文".decode('utf-8'))   #"中文".decode('utf-8')是unicode类​

五、区别-python2/python3的str、bytes类型

python2与python3在定义str、bytes类型时是通用的。

#定义str类型
s = 'test'
s = "test"
s = '''test'''
s = """test"""
​
#定义bytes类型
b = b'test'
b = b"test"
b = b'''test'''
b = b"""test"""

首先, bytes类型是字节序列,一个事实上的bytes类型每个元素就是一个字节 。

打开一个gbk编码的文本,可以看到其对应十六进制字符码。

理解Python的编码问题

 

utf-8编码的文本,字符码则不同。可以看到, 能表示更多字符的编码标准,需要更多的字节来表示字符 

理解Python的编码问题

 

首先查看如下python2代码运行情况。

理解Python的编码问题

 

可以看到,python2处理str和bytes时是混用的,可以进行+运算,但它们都是字节序列。 python2没有将str和bytes型数据做明显的区分,是一种隐式的混用,并且python2处理str类型时优先将其视为bytes 。事实上str.decode是bytes.decode,从而转换成unicode。 str/bytes/unicode三者关系:str(bytes)—decode—>unicode—encode—>str(bytes) 。

不同于python2, python3对str和bytes型数据作了明显区分,str表示文本,默认就是原生unicode的utf-8编码格式,bytes型数据就表示二进制数据 ,用下标取bytes类型的单个元素返回的是int类型,而在python2中用下标取bytes类型的单个元素返回的还是bytes。 bytes/str/unicode三者关系:bytes—decode—>str(unicode)—encode—>bytes ,不能像python2那样string.decode。

s = '中文'
b = bytes(s,encoding='utf-8') # s.encode('utf-8')
s = str(b,encoding='utf-8') # b.decode('utf-8')

上面python2的实验,标准输出编码是gbk,将\xd6\xd0\xce\xc4复制到python3验证编码区别,如下python3的运行情况,可以看到gbk可以正常解码,再进行encode(‘utf-8′)得到bytes字节序列是不是与前面utf-8文本中”中文”的十六进制数据相同。

理解Python的编码问题

 

六、文件路径/文件名/文件读写

1.文件路径的困惑

存在如下一段代码,遍历目录里面的文件。

# -*- coding: UTF-8 -*-

import os

all_files = []
for filepath, dirnames, filenames in os.walk(directory):
	for filename in filenames:
		tmppath = os.path.join(filepath, filename)
		all_files.Append(tmppath)
print(all_files)

python2/python3结果对比。python3将str视为unicode处理的,所以正常显示。可以看到python2是以gbk编码来识别目录的。

理解Python的编码问题

 

当前活动代码页是gbk,那python2识别目录是否会受到当前cmd终端编码方式影响?改变当前活动代码页为utf-8后,目录还是以gbk编码识别,encode成utf-8才能正常打印出目录名。

理解Python的编码问题

 

文章开头了解到ANSI编码的特点,是否可以推测python2是以当前系统ANSI编码获取目录名的呢?linux shell验证确实如此。

理解Python的编码问题

 

2.python2的文件名乱码

python2运行如下代码,文件名出现了乱码。

# -*- coding: UTF-8 -*-
# python2/python3

with open('中文2.txt','w+') as f:
	f.write("中文")​
理解Python的编码问题

 

前面推测python2是以当前系统ANSI编码获取目录名,所以创建文件是否也是这样,由于声明了编码方式是utf-8,而文件名”中文2.txt”字符串是以bytes、utf-8格式存储的,与gbk不一致,所以导致乱码。尝试将代码改成如下,文件名正常。

# -*- coding: UTF-8 -*-
# python2

with open('中文2.txt'.decode('utf-8').encode('gbk'),'w+') as f:  #当前编码是utf-8 所以先decode,再encode为系统的gbk编码
	f.write("中文")
理解Python的编码问题

 

那么找文件读又会是怎样?

# -*- coding: UTF-8 -*-
# python2

with open('中文2.txt','r') as f:
	print(f.read().decode('utf-8'))

python2执行报错No such file or directory,没有文件或目录。

理解Python的编码问题

 

修改文件名为 ‘中文2.txt’.decode(‘utf-8′).encode(‘gbk’) 后才正常找到了文件,由此可见, python2在识别目录、open创建、读取文件时均以系统ANSI编码识别的 ,处理中文名称时, 需要将目录/文件名字符串先解码,再编码成系统的ANSI编码格式 。

3.写入是否正常

打开前面python2生成的两个文件,都是utf-8格式,内容中文显示正常!体会到python2的诡异编码了吗?至于这一点,忘了吧…

python3就没那么奇葩了,写入文本后,中文windows系统是ANSI编码,而linux是utf-8编码。

理解Python的编码问题

 


理解Python的编码问题

 

python3 open文件操作若不指定编码,则默认以系统ANSI编码写入、读取文本。 建议如果不是以二进制读取和写入,open文件时可指定文本的编码方式。

f = open("中文1.txt",'a+',encoding='utf-8')

4.读取小结

不管python2还是python3,解决编码问题的核心都是要解决编码统一。通过”python2/python3的默认编码”小节,我们认识到python2与python3默认编码的区别,实际上 python文件开头的编码声明声明的是当前脚本内字符串的编码 ,所以才有了python2打印输出中文时需要先decode(‘utf-8′),再encode(‘gbk’)为cmd终端编码格式,以及中文文件名的编码转换,至于python3,统一了编码,str就是原生unicode,具有普适性,就没有那么多编码转换。总之,读取文件时,需要明确文件的编码,当前python脚本文件的编码声明,输出的编码。如果一个utf-8格式文本文件内包含”\xd6\xd0\xce\xc4″、”\u4e2d\u6587″等字符串,读取后又如何处理呢?

理解Python的编码问题

 

python2 可以以string-escape和unicode-escape方式解码。

# -*- coding: UTF-8 -*-
# python2

with open('a.txt','r') as f:
	lines = f.readlines()
	print(lines)  #读取后会加上转义 ['\xd6\xd0\xce\xc4rn', '\u4e2d\u6587']
	for line in lines:
		if '\x' in line:
			print(line.decode('string-escape'))
		if '\u' in line:
			print(line.decode('unicode-escape'))

python3 可以参考如下方式处理。

# -*- coding: UTF-8 -*-

import codecs

with open('a.txt','r',encoding='utf-8') as f:
	lines = f.readlines()
	print(lines)
	for line in lines:
		if '\x' in  line:
			bline = bytes(bytearray.fromhex(line.strip().replace('\x','')))
			print(bline.decode('gbk'))   #\xd6\xd0\xce\xc4 是"中文"gbk格式的字符码
		if '\u' in line:
			print(codecs.decode(line,'unicode_escape'))

七、python2 json的问题

存在如下代码。

# -*- coding: UTF-8 -*-
# python2

import json

a = {'a':'test','语言':'中文'}
with open('a.txt','a+') as f:
	f.write(json.dumps(a))

运行之后,json.dumps会将中文以unicode的字符码形式dump,并不是真正的中文,需要指定ensure_ascii=False参数来dump真正的中文。

json.dumps(a,ensure_ascii=False)

如下,dumps后文件为utf-8格式,如果读取进行json.loads,得到的字典”键”和”值”就都会是unicode类型的。

理解Python的编码问题

 

# -*- coding: UTF-8 -*-

import json

with open('a.txt','r+') as f:
	print(json.loads(f.read()))

结果如下。

理解Python的编码问题

 

需要对获取到的字典”键”和”值”进行解码的话,这里可以参考一段代码处理。

def unicode_convert(input):
	if isinstance(input, dict):
		return {unicode_convert(key): unicode_convert(value) for key, value in input.items()}
	elif isinstance(input, unicode):
		return input.encode('utf-8')
	else:
		return input

八、总结

4月20日,Python2的最后一个版本发布:2.7.18。可以说python2已是过去式,python3才是未来。可为什么文章大部分内容却还是python2的呢?一是确实python2的字符编码问题多,解决这些问题能更好的理解python编码机制;二是即便python2不再有,但编码问题一定一直会存在,不管是python自己生成处理的数据还是其它源数据。从解决python2的编码问题到了解python2与python3的差异,总结出以下解决编码问题的关键点,如有不当,还望指正。

1.python2的默认编码是ascii,python3则是utf-8。

2.python文件开头的编码声明声明的是当前脚本内字符串的编码,要避免编码错误,需要统一数据源,声明的编码类型,数据输出三者的编码。

3.python2没有将str和bytes型数据做明显的区分,是一种隐式的混用,并且python2处理str类型时优先将其视为bytes。str/bytes/unicode三者关系:str(bytes)—decode—>unicode—encode—>str(bytes)。

4.python3对str和bytes型数据作了明显区分,str表示文本,默认就是原生unicode的utf-8编码格式,bytes型数据就表示二进制数据。bytes/str/unicode三者关系:bytes—decode—>str(unicode)—encode—>bytes。

5.python2在识别目录、open创建、读取文件时均以系统ANSI编码识别的。

6.python3 open文件操作若不指定编码,则默认以系统ANSI编码写入、读取文本。



Tags:Python编码   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
编码封装的好处:没有安装pycharm的电脑也可以运行代码,适合接单的同学,可以将封装好的代码发给客户。步骤如下: 第一步 安装库pip install pyinstaller因为程序自身存在缺陷,首先...【详细内容】
2021-12-07  Tags: Python编码  点击:(19)  评论:(0)  加入收藏
一、字符编码初探字符编码其实就是将人类能识别的字符与计算机能识别的数字对应起来。ASCII(American Standard Code for Information Interchange)美国信息交换标准代码,是...【详细内容】
2020-07-05  Tags: Python编码  点击:(43)  评论:(0)  加入收藏
本节收录了稍作剪辑的PEP 8摘要(Python Enhancement Proposal,Python增强提案)。PEP 8由Guido van Rossum和Barry Warsaw撰写,是Python的最接近编程风格手册的东西。这里省略了...【详细内容】
2020-06-05  Tags: Python编码  点击:(47)  评论:(0)  加入收藏
如果说在python2中处理字符编码很蛋疼的话,如果幻想着python3不那么蛋疼,那么我只想说,你想多了,好不容易在python2中把字符编码的问题捣腾清楚了,但是换成python3,它会将之前的...【详细内容】
2019-08-27  Tags: Python编码  点击:(242)  评论:(0)  加入收藏
▌简易百科推荐
本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作。前...【详细内容】
2021-12-28  linux技术栈    Tags:glibc   点击:(3)  评论:(0)  加入收藏
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(2)  评论:(0)  加入收藏
程序是如何被执行的  程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(10)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(20)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(25)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(25)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条