一文彻底搞懂 Python 字符编码
一. 编码与解码
计算机中存储的信息都是二进制的,编码/解码本质上是一种映射(对应关系),比如字符 'a' 用 ascii 编码则是十进制 65,计算机中存储的就是二进制 00110101,但是显示的时候不能显示 00110101,还是要显示 'a',但计算机怎么知道 00110101 是 'a' 呢,这就需要解码,当选择用ascii解码时,当计算机读到 00110101 时就到对应的ascii 表里一查发现是 'a',就显示为 'a'。
- 编码:真实字符与二进制串的对应关系,真实字符→二进制串
- 解码:二进制串与真实字符的对应关系,二进制串→真实字符
二. 编码格式分类
ascii、gb2312、gbk、unicode、utf-8 这些编码都分别表示什么含义呢 ?
ascii编码
ASCII(American Standard Code for Information Interchange,美国信息互换标准代码,ASCⅡ)是一套计算机编码系统。因为计算机只能识别数字,所以有了编码,将字符转换成成对应的数字。共定义了128个字符,对应数字是0-127, 其中33个字符无法显示,在33个字符之外的是95个可显示的字符(包括空格)。
- 字符0-9 ---对应--- 十进制 48-57
- 小写字母a-z ---对应--- 十进制 97-122
- 大写字母A-Z ---对应--- 十进制 65-90
ascii编码规定,一个英文字符需要一个字节(8bit位)大小,汉字的编码超出了ascii的范围,至少需要两个字节来表示一个中文字符,某些复杂的汉字需要3到6个字节来表示。所以中国制定了GB2312编码,用来把中文编进去。
gb2312编码
gb2312编码是第一个汉字编码国家标准,由中国国家标准总局1980年发布,1981年5月1日开始使用。GB2312编码共收录汉字6763个,其中一级汉字3755个,二级汉字3008个。同时,gb2312编码收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。
gb2312编码范围:A1A1-FEFE,其中汉字编码范围:B0A1-F7FE。
gbk编码
GBK编码,是对GB2312编码的扩展,因此完全兼容GB2312-80标准。GBK编码依然采用双字节编码方案。共收录汉字和图形符号21886个,其中汉字(包括部首和构件)21003个,图形符号883个。GBK编码方案于1995年12月15日正式发布,这一版的GBK规范为1.0版。
unicode编码
unicode编码是将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,unicode存在的问题就是存储英文时非常浪费存储,大部分是的bit位是空的。
utf-8编码
UTF-8 编码把一个 Unicode 字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。UTF-8编码有一个额外的好处,就是 ASCII 编码实际上可以被看成是UTF-8编码的一部分,所以,大量只支持 ASCII 编码的历史遗留软件可以在 UTF-8 编码下继续工作。
三. 数据的存在形式
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件。
四. Python2 字符编码
如何判断字符编码
Python2 把字符串分为 unicode、str 两种类型:
a="aaa" <type 'str'>
str 本质上是一种字节码b=u"aaa" <type 'unicode'>
s = "你好yuchaoshui"
s
变量是个字节码字符串,它本身存储的就是字节码。那么这个字节码是什么格式的?要根据具体的环境来看:
- 如果这段代码是在解释器上输入的:
那么这个s的格式就是解释器的编码格式,对于windows的cmd而言,就是gbk,对于Linux来说,就是ascill。当 s 字符串的值不全是英文即包括了汉字时,解释器就会自动地将字符串编码成 gbk(windows)、gb2312(windows)、utf-8(linux)的字节流。 - 如果将代码是保存后才执行的:
比如存储为 utf-8(在文件开头写上 * coding:utf-8 * ),这个 utf-8 是 py 文件的编码格式, 在解释器载入这段程序的时候按 utf-8 解码这个文件。不指定时程序默认按照 ASCII 字符集来解码
获取 解释器 的默认编码
import sys
sys.getdefaultencoding()
设置 解释器 的默认编码(极力····不推荐)
reload(sys)
sys.setdefaultencoding('utf-8')
获得 系统 默认编码格式
import sys
sys.getfilesystemencoding()
unicode与字节码转换
把已经编码的数据解码成 unicode 有两种方式。在 Python2中,字符串直接就是字节流的格式数据,所以直接 decode 成 unicode 就行。
# 使用 decode 函数
"你好yuchaoshui".decode("utf-8")
# 使用 decode 函数
"yuchaoshui".decode("utf-8")
# 直接在字符串前面加 u
u"你哈"
把 unicode 格式数据编码为 bytes 类型的字节流
u"你好".encode("utf-8") # 把unicode编码成utf-8
u'\u4f60\u597d'.encode("utf-8") # 效果同上
五. python3 字符编码
如何判断字符编码
对于单个字符的编码,Python 提供了 ord()
函数获取字符的整数表示,chr()
函数把编码转换为对应的字符
ord('A') # 结果是65
ord('中') # 结果是20013,
chr(66) # 结果是'B'
chr(25991) # 结果是'文'。
在最新的Python 3版本中,字符串是以Unicode编码的。
s = "你好yuchaoshui"
s
是个unicode格式字符串,它本身存储的就是unicode格式,所以,如果将代码是保存后才执行的,就没必要在文件开头写 * coding:utf-8 * 了。
获取 解释器 的默认编码
import sys
sys.getdefaultencoding()
设置 解释器 的默认编码
reload(sys)
sys.setdefaultencoding('utf-8')
unicode与字节码转换
把 unicode 表示的 str 编码为 bytes 类型的字节流(两种方式)
name1 = "你好 yuchaoshui" # name1变量是unicode编码的字符串类型str
name2 = "你好 yuchaoshui".encode("utf-8") # name2变量是将字符串的值编码成utf-8的字节流了
name3 = "你好 yuchaoshui".encode("gb2312") # name3变量是将字符串的值编码成gb2312的字节流了
name4 = "hello yuchaoshui".encode("ascii") # name4变量是将字符串的值编码成ACSII的字节流了
name5 = b"hello yuchaoshui" # 字符串被编码成了ASCII表示的字节流
name6 = b"你好 yuchaoshui" # 会出错,使用 b"string" 这种方式时,只能用于英文字符,如果有中文则只能用encode函数编码了
把已经编码的数据解码,解码时必须指定正确的解码方式。
b'ABC'.decode('ascii') # 结果是 'ABC',被解码成unicode
b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8') # 结果是 '中文',被解码成unicode
b'\xe4\xb8\xad\xe6\x96\x877'.decode('utf-8', errors='ignore') # 当解码数据出错时,errors='ignore' 表示忽略出现的错误。
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore') # 在读取文件时也可能出错,同样可以给出errors关键字参数
判断编码方式
判断被编码的数据的编码方式 方法一:用尝试法尝试地去解码,解码成功即可得知编码。
coding = ""
for i in ["ascii", "gbk", "gb2312", "utf-8"]:
try:
your_string.decode(i)
coding = i
break
except:
coding = ""
当try捕捉到异常,也就是不能正常解码时,编码为空,当解码过程顺利时coding被赋值,退出。
判断被编码的数据的编码方式 方法二:用模块判断被编码数据的编码方式。
pip3 install chardet
import chardet
chardet.detect(b'abcd')
chardet.detect("你好".encode("utf-8"))
chardet.detect("你好".encode("gb2312"))
模块判断的方式可能不准确,返回的是一个字典,比如{'encoding': 'utf-8', 'language': '', 'confidence': 0.7525}
有一个字段 confidence 表示准确度。
读写文件数据
从文件读取数据、把数据写入文件
f = open("text.txt", "wb")
string1 = "你好 yuchaoshui"
f.write(string1.encode("utf-8"))
f = open("text.txt", "rb")
for line in f:
print(line.decode("utf-8"))
当我们用"wb"方式打开文件时,写入的数据也必须是二进制的数据(encode过的)
当我们用"rb"方式打开文件时,指定的以二进制方式打开,这时就需要自己去解码。