流畅的python,Fluent Python 第四章笔记
4.1字符问题:
字符的标识即码位,是一串数字,再unicdoe中以4-6个16进制数标识。(其实码位就时字符了,就好比我的名字是小明,小明就是我。再py3,unicode直接输出文字,在py2里print输出unicode也时具体文字)
字符的具体表述取决于所用的编码。编码是在码位和字节序列之间转换时使用的算法。
把码位转换成字节序列的过程叫做编码;把字节序列转换成码位的过程叫做解码。
简单理解,编码时给机器用的,解码后是给人用的。
4.2字节该要
In [687]: b'\xc3\xa9'.decode('U8') Out[687]: 'é' In [688]: cafe = bytes('café', encoding='utf8') In [689]: cafe Out[689]: b'caf\xc3\xa9' In [690]: cafe[0] Out[690]: 99 In [691]: cafe[-1] Out[691]: 169 In [692]: cafe[-1:] Out[692]: b'\xa9' In [693]: cafe = bytearray(cafe) In [694]: cafe Out[694]: bytearray(b'caf\xc3\xa9') In [695]: cafe[0] Out[695]: 99 In [696]: cafe[-1] Out[696]: 169 In [697]: cafe[-1:] Out[697]: bytearray(b'\xa9') In [698]: '12345'[2:3] == '12345'[2] Out[698]: True
通过代码可以看到,bytes与bytesarray的各个元素都是介于0-255(含)【两个16进制数字的区间,二进制111111111,8个1最大就是255】之间的整数。
二进制序列的切片始终是同以类型的二进制序列,包括长度位1的切片。
可迭代对象的切片中,从上面可以看出,只有str可以做到,取值于切片是一样的。
另外的取值是取值,切片出来的一个相同类型的序列,里面包含这个取值的元素。
好比s[i] 是取出迭代中的i值,s[i :i+1]取出的是s[i]
从输出可以看到,字节序列只有三种不同的方式显示
1、可以打印的ASCII范围内的字节,使用ASCII字符自身
2、制表符、换行符、回车符和\,就是\t \n \r \\
3、就是\x啦啦,啦啦为两个16进制数字
字节序列可以用很多字符串的方法,除了那些判断字符串属性的以外,可以简单的认为也是一个字符串,只不过是一个比较特殊的二进制字符串。
In [753]: bytes.fromhex('314bcea9') Out[753]: b'1K\xce\xa9'
这是bytes特有的方法,可以从字符串直接转换为字节序列,31于4b因为刚好对应ASCII的1于K所以直接输出了1于K,当时卡住我老半天。
In [246]: bytes([12,80,56,255]) Out[246]: b'\x0cP8\xff'
bytes后面可以直接跟一个可迭代的数字(0,256),里面的每个元素能直接按照asci码对应的表中取参数,有的直接输出,没有的按照\x已16进制形式输出字节序列。
如何创建一个字节序列对象呢。
1、最直接
In [752]: '!love你,\n'.encode() Out[752]: b'!love\xe4\xbd\xa0,\n'
上面可以看出来,标点符号,英文字母都输出来了,就是中文被输出了另外的。
构建bytes或bytearray实例还可以调用各自的构造方法,传入下述参数。
1、一个str对象和一个encoding关键字参数
2、一个可迭代对象,提供0-255之间的数值(难道就我前面写的fromhex)
3、一个实现了缓冲协议的对象(如bytes、bytearray、memoryview、array.array);此时把源对象中的字节序列复制到新建的二进制序列中。
(啥叫缓冲协议对象,我查不到资料,郁闷。)书中代码上一下
In [754]: numbers = array.array('h',[-2,-1,0,1,2]) In [755]: octets = bytes(numbers) In [756]: octets Out[756]: b'\xfe\xff\xff\xff\x00\x00\x01\x00\x02\x00' In [757]:
不知道这样又有什么意思?而且我试过bytes后面就算不同的0-255之内的数字也可以输出。
In [761]: bytes([1,2,3,4,5,233]) Out[761]: b'\x01\x02\x03\x04\x05\xe9' In [762]:
后面讲了结构体于内存视图
struct于meneryview,struct是用来操作二进制数据的,没咋看懂,就不上代码,也不写了。
4.3基本的编解码器
Python自带100多种解码器,很多。
常用的utf-8可以写成utf8,U8
unicode的字符除了utf-8 utf-16可以全部找到对应的字节次序,另外的都不全,再decode编码的时候,如果碰到自己字符集没有的字符,默认设置就会报错。
4.4了解编码问题
如果你不用utf-8去decode遇到自己没有的字符集就会报错。
In [762]: s = 'abs我cd'
In [763]: s.encode(encoding='adcii')
---------------------------------------------------------------------------
LookupError Traceback (most recent call last)
<ipython-input-763-c481354da0b9> in <module>
----> 1 s.encode(encoding='adcii')
LookupError: unknown encoding: adcii
In [764]: s.encode(encoding='ascii')
---------------------------------------------------------------------------
UnicodeEncodeError Traceback (most recent call last)
<ipython-input-764-8fa11c3441f2> in <module>
----> 1 s.encode(encoding='ascii')
UnicodeEncodeError: 'ascii' codec can't encode character '\u6211' in position 3: ordinal not in range(128)
In [765]: s.encode(encoding='ascii',errors='ignore')
Out[765]: b'abscd'
In [766]: s.encode(encoding='ascii',errors='replace')
Out[766]: b'abs?cd'
In [767]: s.encode(encoding='ascii',errors='xmlcharrefreplace')
Out[767]: b'abs我cd'
In [768]:
有三个参数可以选择,最后一个参数有意思,网页上面尽然又显示出来这个我字了
In [767]: s.encode(encoding='ascii',errors='xmlcharrefreplace') Out[767]: b'abs我cd'
书中还说,error的参数是可扩张的。
当然decode也一样了
In [770]: s Out[770]: 'abs我cd' In [771]: s.encode('U8').decode('ascii',errors='ignore') Out[771]: 'abscd' In [772]: s.encode('U8').decode('ascii',errors='replace) File "<ipython-input-772-ed017d19dba1>", line 1 s.encode('U8').decode('ascii',errors='replace) ^ SyntaxError: EOL while scanning string literal In [773]:
用用utf-8时建议写replace,因为会给一个默认的替代字符,这笔调过这个字好多了
4.4.4如何找出字节序列的编码
用chardet.detect
In [774]: string = '我是大叔阿瓜' In [775]: import chardet In [776]: gg = string.encode('gbk') In [777]: uu = string.encode() In [778]: chardet.detect(gg) Out[778]: {'encoding': None, 'confidence': 0.0, 'language': None} In [779]: chardet.detect(uu) Out[779]: {'encoding': 'utf-8', 'confidence': 0.99, 'language': ''} In [780]: chardet.detect('师傅登革热特哥时代风范厄尔人'.encode('gbk')) Out[780]: {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'} In [781]:
字符数据越多越准确。
4.5处理文本文件。
在用open处理文本文件的时候,encoding不要使用系统默认的,容易出问题。
然后就是尽量使用linux系统电脑,因为系统里面所有的编码,解码都是基于utf-8的,
这样可以字节序列于字符编码,解码时候的错误。
我们处理文档默认的编码为:
In [810]: locale.getpreferredencoding() Out[810]: 'UTF-8'
4.6为了正确比较而规范化Unicode字符串(感觉用到的机会不大)
后面基本都是基于国外文字,德文,拉丁文相关的unicdoe处理,我稍微看了下。就不写了。