python文件操作(二)
一、二进制处理文件
通过前面的说明,我们知道‘b’模式是通过字节的形式来读写文件,但是要理解一点的是,这种模式只是在内部处理的时候是字节,但是我们打开文件看到的还是字符串的形式,而不是一堆字节。还有一点需要了解的是,在linux系统里面,‘b’模式是没有作用的,因为linux理念的是一切皆文件,他本来所有的文件都是通过字节操作的,所以如果要跨平台处理文件,需要用到‘b’模式。
1.‘rb’模式
下面就来演示‘b’模式读取一个py文件,py文件的内容如下:
hello
11111
22222
33333
根据前面的内容,我们一般这么写:
f=open('test11.py','rb',encoding='utf-8') #b的方式不能指定编码 data=f.read() print(data) f.close()
但是运行后会发现报错,ValueError: binary mode doesn't take an encoding argument,b的方式不能指定编码,为什么不能指定?因为你文件存在硬盘的方式就是一堆二进制,之所以你能看到字符串,是因为你是用应用程序打开的文件,应用程序有不同的编码方式,所以如果你是‘r’模式去读,要区分编码,如果是‘rb’模式去读,读的是原生的二进制,就不用应用程序编码了,所以会报错。
所以正确的写法是这样的:
f=open('test11.py','rb') #b的方式不能指定编码 data=f.read() print(data) f.close()
拿到的结果是:
b'hello\r\n11111\r\n22222\r\n33333'
前面这个b,就说面这一个字节内容,如果在源文件加一列中文,结果会是:
b'hello\r\n11111\r\n22222\r\n33333\r\n\xe4\xbd\xa0\xe5\xa5\xbd'
如果你想看到你原本写的字符串,就要对结果进行解码(对应你写入字符串时的编码,我这里写入时为‘utf-8’编码),也就是decode:
f=open('test11.py','rb') #b的方式不能指定编码 data=f.read() #'字符串'---------encode---------》bytes #bytes---------decode---------》'字符串' # print(data) print(data.decode('utf-8')) f.close()
这时看到的结果就是:
hello 11111 22222 33333 你好
这里有一个小的补充:windows里面的回车\r\n,linux和unix的回车则直接是\n,所以跨系统传文件打开后看到的会有一些偏差,原因可能就是这个。
2.‘wb’模式
读文件我们会了,写文件也是一样的,我们写入也要写入bytes形式的,字符串转换成字节可以通过bytes()函数实现。
f=open('test22.py','wb') #b的方式不能指定编码 f.write(bytes('1111\n',encoding='utf-8')) f.close()
运行完之后我们拿到一个新文件,里面的内容就是1111,这里面写中文也是可以的。字符串转字节是一种编码过程,那我们是不是可以用encode方法呢?
f=open('test22.py','wb') #b的方式不能指定编码 # f.write(bytes('你好\n',encoding='utf-8')) f.write('你好'.encode('utf-8')) f.close()
运行完之后,结果同样OK。‘ab’模式用法就不多说了,和前面的a类似。现在有一个疑问,为什么我们要用b模式来处理文件?那我用r/w/a也可以完成上面的操作。原因有两点:
1.文件不仅仅只有文本这一种形式,还有图片视频等,这些非文本形式你用r/w/a处理不了;
2.就是之前说过的,你需要跨平台处理文件的时候,不管你什么平台,你最后处理的都是一堆二进制;
二、文件操作的其他方法
操作文件的方法除了之前说read、readlines、readline、write等,还有其他方法可以了解一下:
1.f.encoding (如果文件打开模式为b,则没有该属性)
f=open('a.txt','r+',encoding='gb2312') print(f.encoding) f.close() >>>gb2312
这里拿到的文件打开的编码,切记,和源文件以什么方式存放到硬盘的方式无关。无法解决你因为不知道源文件编码时,打开文件乱码的问题。不过这种情况很少见,大部分情况你都是知道源文件编码的,如果真的遇到这种情况,就可以用一种encoding='latin-1'的编码去打开,这种编码兼容了大部分的编码方式。
2.f.flush() (立刻将文件内容从内存刷到硬盘)
当你通过open打开一个文件的时候,你写的东西其实都是内存里面,并没有保存在硬盘中,你不保存,这些数据就一直在内存中,假如关闭或断电了就没了。可是你会发现这种情况在pycharm里面,你没有保存,文件内容断电之后也还存在,这是因为这些应用程序每隔几秒就自动帮你保存一次,这种保存的方法就是flush(),你可以在cmd里面实验这种情况。
3.f.tell() (返回文件光标所在的位置)
现在有一个文件,就写了一行hello,看看tell返回什么
f=open('b.txt','r',encoding='utf-8') print(f.tell()) f.readline() print(f.tell())
>>>0
>>>5
返回的就是当前光标的位置,注意当有多行时,会加上回车的两个字节。
4.python做的好事!
上面我们说过,Windows的回车是\r\n,下面看一下:
f=open('b.txt','r',encoding='utf-8') print(f.readlines()) f.close() >>>['hello\n', '11111']
怎么我打印出来只有\n呢?这就是python帮你处理过的结果,想要拿到真正的结果,还要加上一个newline=''
f=open('b.txt','r+',encoding='utf-8',newline='') #读取文件中真正的换行符号 print(f.readlines()) f.close() >>>['hello\r\n', '11111']
这样拿到的才是真实的结果,不过这没什么用,了解一下就行。
5.f.seek()
在讲这个之前,要先了解一下光标移动的两个点:
一: read(3):
1. 文件打开方式为文本模式时,代表读取3个字符
2. 文件打开方式为b模式时,代表读取3个字节
二: 其余的文件内光标移动都是以字节为单位如seek,tell,truncate
f=open('b.txt','r+',encoding='utf-8') f.seek(1) print(f.tell()) f.seek(3) print(f.tell()) >>>1 >>>3
这就很容易理解了,但是如果你是中文开头的,一个中文是3个字节,你seek(1)的话就要报错了。还有一点就是,这里的seek都是以0为基准进行移动的。这种是默认情况,seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的。建议试一下read和seek的区别。
f=open('b.txt','rb') print(f.tell()) f.seek(10,1) print(f.tell()) f.seek(3,1) print(f.tell()) >>>0 >>>10 >>>13
从上可知,seek模式改为1,就变为相对位置了。再改为2试试:
f=open('b.txt','rb') print(f.tell()) f.seek(-5,2) print(f.read()) print(f.tell()) f.seek(3,1) print(f.tell()) >>>0 >>>b'11111' >>>12 >>>15
模式2就是倒着seek,倒着移动的话,里面的参数也要写成负数,注意了。那么这个seek有什么用呢,就要在实际场景中去思考了。
6. f.truncate([size]) 把文件裁成规定的大小
如果size比文件大小还大, 这个会根据系统的不同可能不改变文件,可能补充0进去,或者补充一些随机的内容。truncate(10)就是从开头截取到第10个字节。所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下进行。
其他方法很少用到,了解一下就行了。