目录
2019/08/07 学习整理
Python文件处理
字符编码
一、什么是字符编码
计算机要想工作必须通电,即用‘电’驱使计算机干活,也就是说‘电’的特性决定了计算机的特性。电的特性即高低电平(人类从逻辑上将二进制数1对应高电平,二进制数0对应低电平),关于磁盘的磁特性也是同样的道理。结论:计算机只认识数字。
很明显,我们平时在使用计算机时,用的都是人类能读懂的字符(用高级语言编程的结果也无非是在文件内写了一堆字符),如何能让计算机读懂人类的字符?
必须经过一个过程:
- 字符 --------》翻译过程 -------》数字
总而言之,字符编码是将人类的字符编码成计算机能识别的数字,这种转换必须遵循一套固定的标准,该标准无非是人类字符与数字的对应关系,称之为字符编码表。
二、涉及到字符编码的两个场景
- 一个Python文件中的内容是由一堆字符组成的,存取均涉及到字符编码问题(Python文件并未执行,前两个阶段均属于该范畴)。
- Python中的数据类型字符串是由一串字符组成的(Python文件执行时,即第三个阶段)。
三、字符编码的发展史与分类
计算机由美国人发明,最早的字符编码为ASCII,只规定了英文字母数字和一些特殊字符与数字的对应关系。最多只能用 8 位来表示(一个字节),即:2**8 = 256,所以,ASCII码最多只能表示 256 个符号。
当然我们编程语言都用英文没问题,ASCII够用,但是在处理数据时,不同的国家有不同的语言,中国人会加入中文,日本人会在自己的程序中加入日文,韩国人也是。
但是要表示中文,单拿一个字节表表示一个汉子,是不可能表达完的(连小学生都认识两千多个汉字),解决方法只有一个,就是一个字节用>8位2进制代表,位数越多,代表的变化就多,这样,就可以尽可能多的表达出不通的汉字。
所以中国人规定了自己的标准gb2312编码,规定了包含中文在内的字符与数字的对应关系。
日本人规定了自己的Shift_JIS编码;韩国人规定了自己的Euc-kr编码(另外,韩国人说,计算机是他们发明的,要求世界统一用韩国编码,但世界人民没有搭理他们)。
这时候问题出现了,精通18国语言的小周同学谦虚的用8国语言写了一篇文档,那么这篇文档,按照哪国的标准,都会出现乱码(因为此刻的各种标准都只是规定了自己国家的文字在内的字符跟数字的对应关系,如果单纯采用一种国家的编码格式,那么其余国家语言的文字在解析时就会出现乱码)。所以迫切需要一个世界的标准(能包含全世界的语言)于是Unicode应运而生(韩国人表示不服,然后没有什么卵用)。
ascii用1个字节(8位二进制)代表一个字符;Unicode常用2个字节(16位二进制)代表一个字符,生僻字需要用4个字节。
例:字母x,用ascii表示是十进制的120,二进制0111 1000。
汉字中已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。
字母x,用Unicode表示二进制0000 0000 0111 1000,所以Unicode兼容ascii,也兼容万国,是世界的标准。
这时候乱码问题消失了,所有的文档我们都使用但是新问题出现了,如果我们的文档通篇都是英文,你用Unicode会比ascii耗费多一倍的空间,在存储和传输上十分的低效。
本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8(Unicode Transformation Format-8)编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:
字符 | ASCII | Unicode | UTF-8 |
---|---|---|---|
A | 01000001 | 00000000 01000001 | 01000001 |
中 | x | 01001110 00101101 | 11100100 10111000 10101101 |
从上面的表格还可以发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上可以被看成是UTF-8编码的一部分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。
四、乱码分析
首先明确概念
- 文件从内存刷到硬盘的操作简称存文件
- 文件从硬盘读到内存的操作简称读文件
乱码的两种情况:
乱码一:存文件时就已经乱码
存文件时,由于文件内有各个国家的文字,我们单以shiftjis去存,
本质上其他国家的文字由于在shiftjis中没有找到对应关系而导致存储失败。但当我们硬要存的时候,编辑并不会报错(难道你的编码错误,编辑器这个软件就跟着崩溃了吗???),但毫无疑问,不能存而硬存,肯定是乱存了,即存文件阶段就已经发生乱码,而当我们用shiftjis打开文件时,日文可以正常显示,而中文则乱码了。
乱码二:存文件时不乱码而读文件时乱码
存文件时用utf-8编码,保证兼容万国,不会乱码,而读文件时选择了错误的解码方式,比如gbk,则在读阶段发生乱码,读阶段发生乱码是可以解决的,选对正确的解码方式就ok了。
文件的三种打开方式
r模式为read
r: read,只读模式,只能读不能写,文件不存在时报错。
f = open('32.txt', mode='r') # 报错 f.write() f.close()
# rt: read by text # windows的操作系统默认编码为gbk,因此需要使用utf8编码 f = open('32.txt', mode='rt', encoding='utf8') data = f.read() print(data) print(f"type(data): {type(data)}") f.close()
aaa bbb ccc 胡歌最帅吗 type(data): <class 'str'>
# rb: read by bytes f = open('32.txt', mode='rb') data = f.read() print(data) print(f"type(data): {type(data)}") f.close()
b'aaa\nbbb\nccc\nnick\xe6\x9c\x80\xe5\xb8\x85\xe5\x90\x97' type(data): <class 'bytes'>
f.read()读取文件指针会跑到文件的末端,如果再一次读取,读取的将是空格。
f = open('32.txt', mode='rt', encoding='utf8') data1 = f.read() data2 = f.read() print(f"data1: {data1}") print(f"data2: {data2}") f.close()
data1: aaa bbb ccc 胡歌最帅吗 data2:
由于f.read()一次性读取文件的所有内容,如果文件非常大的话,可能会造成内存爆掉,即电脑卡死。因此可以使用f.readline()/f.readlines()读取文件内容。
# f.readline()/f.readlines() f = open('32.txt', mode='rt', encoding='utf8') print(f"f.readable(): {f.readable()}") # 判断文件是否可读 data1 = f.readline() data2 = f.readlines() print(f"data1: {data1}") print(f"data2: {data2}") f.close()
f.readable(): True data1: aaa data2: ['bbb\n', 'ccc\n', '胡歌最帅吗']
w模式为write
w: 只能写,不能读,文件存在的时候回清空文件后再写入内容;文件不存在的时候会创建文件后写入内容。
# wt f = open('34w.txt', mode='wt', encoding='utf8') print(f"f.readable(): {f.readable()}") f.write('胡歌 真帅呀\n') # '\n'是换行符 f.flush() # 立刻将文件内容从内存刷到硬盘 f.close()
f.readable(): False
# wb f = open('34a.txt', mode='wb') f.write('胡歌 真帅呀'.encode('unicode_escape')) # 编码成bytes类型 print( f"type('胡歌 真帅呀'.encode('unicode_escape')): {type('胡歌 真帅呀'.encode('unicode_escape'))}") f.close()
type('胡歌 真帅呀'.encode('unicode_escape')): <class 'bytes'>
a模式为append
a: 可以追加。文件存在,则在文件的末端写入内容;文件不存在的时候会创建文件后写入内容。
# at f = open('34a.txt', mode='at', encoding='utf8') print(f"f.readable(): {f.readable()}") f.write('nick 真帅呀\n') # '\n'是换行符 f.write('nick,nick, you drop, I drop.') f.write('nick 帅的我五体投地') f.close()
f.readable(): False
文件打开模式之b模式
b模式是通用的模式,因为所有的文件在硬盘中都是以二进制的形式存储的,需要注意的是:b模式读写文件,一定不能加上encoding参数,因为二进制无法再编码。
try: import requests response = requests.get( 'http://images.cnblogs.com/cnblogs_com/nickchen121/1447696/o_文件的三种打开模式-mv.jpg') data = response.content f = open('mv.jpg', 'wb') f.write(data) print('done...') f.close() except Exception as e: print(e, '报错了,那就算了吧,以后爬虫处会详细介绍')
done...
f = open('34w.txt', 'wb') f.write('nick 好帅啊'.encode('utf8')) f.close()