已复制
全屏展示
复制代码

一文彻底搞懂 Python 字符编码


· 8 min read

一. 编码与解码


计算机中存储的信息都是二进制的,编码/解码本质上是一种映射(对应关系),比如字符 '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"方式打开文件时,指定的以二进制方式打开,这时就需要自己去解码。

🔗

文章推荐