说实话,本来是非常不想用 Python2 的,但是架不住服务器上大多都是自带的 Python2,而且还是内网环境,想要到处部署 Python3 的话,再加上装依赖的话,是比较麻烦的。所以不得不用。但是 Python2 的字符集处理也是非常让人头疼的内容。
字符集与编码
字符集,指的是一组符合的集合。在 MFC中的字符宏与CString 已有很多说明了。
通常,我们在提到一种编码的时候,实际上指的是编码集,Code Pages,隐含的也表示了它的字符集,比如:utf8,gbk,ascii 等。
码表中,每个符号都会有一个整数表示的,这个整数,我们叫它为字符的 码点, CodePoints。
不同的字符集中,相同的符号,可能拥有不同的 码点
对于计算机来说,其实他不认识符号,所以我们的符号最终都是,通过存储在计算机的字节中来表示的。
因此,对于一个符号,在不同的编码集中,有着不同的码点,我们需要指定一个符号采用哪一个字符集编码。
比如说对汉字 中,其 Unicode 码点是 4e2d,而在 GBK 中,却是 d6d0
所以,如果我们不对符号指定正确的编码,那么肯定就会出错了。
Unicode 与 UTF_8
Unicode 是对字符到整数代码的编码表示,而 UTF-8,则是如何将这个整数编码在计算机进行存储,理解这点很重要。
你可能会奇怪,码点,与编码有什么区别呢?有区别,很多的时候,你可能看不出什么来,但是当你用一个比较不常见的字的时候,就会发现了。
比如 扊
字,它的码点是 25162
,16 进制就是 0x624a
,而他的 UTF-8 编码则是 \xe6\x89\x8a
,使用了三个字节。
Python2
在 Python2 中,默认使用的是 ASCII 编码,而是,Python2 中的字符串实际上是 bytes
,字节序列。
提前注意:
repr()
,__repr__()
实际上打印出来的是,计算机进行识别和表示的内容str()
,__str__()
实际上是打印出来的是,人类可读的形式。
因此,我们就来探究一下这个问题。我们一个字 中 来表示。
s = '中' |
其实我开始也在疑问,为什么默认编码是 ASCII 的情况下,不报多,而是显示三个字节的内容呢?
实际上这三个字节的内容,正是中的 UTF-8 编码,我怀疑是因为在 ASCII 无法识别符号的情况下,根据我的环境变量来将其解释为 utf-8 的。
某个字符想要转换成另外一个种字符集中的编码,是不能直接进行的,必须先转换成 Unicode 码点,然后再转换到其他编码:
s.decode('utf-8').encode('gbk') |
再次重申一点,对于 Python2 ,默认是 ASCII 编码,当遇到识别不了的的符号时,在交互式运行解释器的情况下,可能会应用系统的编码来处理。之所以这样说,是因为如果我们以脚本的形式执行的话,如果不指定编码,就会报错。
对一个符号,实际上 Python2 已经用他自己认为的方式,转换成了字节序列存在在内存中,我们必须首先将其转换成 Unicode 码点,再进行转换到其他编码。
Python3
Python3 有所不同,所有的符号都存储为 Unicode 码点,因此,他已经不再拥有 decode
方法,而只有 encode
方法。
s = '中' |
计算机已经能直接识别这个符号了。这个时候,字符的操作和处理方式,就没有 Python2 这么繁琐了。但我们同样看出他存储的内容是什么:
hex(ord(s)) |
ord 用来取出一个字符在计算机内的存储 Unicode 码点。在 Python2 中这个只能接受单字符(因为他是 ASCII 字节序列存储的),而 Python3 就随便搞了(所有的 Unicode 符号都可以)。
将符号转换为 ASCII 进行保存。
比如,我们在传输数据的时候,为了减少麻烦,做一下简单的加密。就会有字符串,处理成 ASCII 的格式在 HTTP 上进行传输。Python2 和 Python3 的实现就会有所不同。
现在,我们约定,我们使用的中文是 GBK 编码。
Python2
这里,我们的中文,将会使用 ‘utf-8’ 进行编码,存储在字节序列中,我们要先将其转换成 GBK 编码的序列
s = '中' |
Python3
s = '中' |
这也验证了,在 Python2 中,其实 str 就是字节序列。而 Python3 中,str 与 bytes 是不同的
Unicode 码点
对于一个中文符号,其实我们可以查看其在 Python3 的内部是如何存储的:
s = '中' |
一个整型值。这与所有符号都是有一个码点来编码的一致。
字面值
字符串和字节串字面值
对于字符串,我们经常会见到很多不同开头的前缀。这代表的是 字面值 的意思,包括字符串字面值和字节串字面值。
字符串字面值前缀:
"r" | "u" | "R" | "U" | "f" | "F" |
字节串字面值前缀:
"b" | "B" | "br" | "Br" | "bR" | "BR" | "rb" | "rB" | "Rb" | "RB" |
实际上就是这么几个:
b
,B
表示是字节串。只能是 ASCII 的字符r
,R
原始字符串,其中的反斜杠不会被转义u
,U
Unicode 字节串f
,F
格式化字符串。可以r
,R
连用,但是不能与b
,B
,u
,U
连用
整字字面值
- 0{ b | B | o | O | x | X} 二进制,8 进制,16 进制值
77e10
这种是浮点字面值
内置函数
int
从字符串构建整数。
int('0x77',16) |
bin
将整数转换为二进制字符串
bin(118) |
注意,这个时候已经是字符串了
hex
将整数转换成 16 进制字符串:
hex(12) |
位操作
位操作之前我还纠结了很久,其实直接用整型进行操作就行了。
缘由是我们要和一个打印机通讯获取其状态,不过通过 SNMP 获取的他的状态,有的时候是 0x00
这样表示正常,但是返回的的异常状态却可能会是 A
,B
这样的情况,是将一个 16 进制的值,解释成了字符串。返回的是一个 16bit,2 字节的值,每一位代表一个信息位。因此,最终我研究出来的办法是,先将此字符串,转换成其代表的值,然后进行相关的与或操作。
st = snmp.get() # 位代码 |