第五章 文件
程序如果想把数据永久保存在磁盘上,需要通过文件来保存
之前我们学的技术,编写程序时产生的数据都在内存中,当程序结束时,所有的数据都被清空,达不到永久保存的效果
狭义的文件就是指保存在硬盘上的数据
广义的文件是指一切IO,就是有输入和输出的接口,比如键盘,网络,显示器,都可以叫文件
5.1 打开文件
在python中打开一个文件非常简单,使用内置的函数 open, 不过打开文件前,要先了解一下文件读取的模式
| 模式 | 含义 |
| r | 读取模式(默认) |
| w | 写入模式 |
| a | 追加模式 |
| b | 二进制模式(与其它模式一起用) |
| t | 文本模式(默认)(与其它模式一起用) |
| + | 读写模式(与其它模式一起用) |
常见的文件打开模式
| 模式 | 含义 |
| r |
以文本方式打开文件,默认使用UTF-8解码 文件必须存在 文件打开后只可以读不可以写 |
| w |
以文本方式打开文件,默认使用UTF-8编码 如果文件存在会清空文件内容 文件打开后只能写不能读 文件打开后默认在文件开头处写 |
| a |
以追加方式打开文件,默认使用UTF-8编解码 文件必须存在 文件打开后只能写,而且写的时候总是在文件最后写 |
| r+ |
以读写方式打开文件,文件必须存在,不会清空文件内容 文件打开后可写可读 文件打开后默认在文件开头处写 |
| w+ |
以读写方式打开文件,如果文件存在会清空文件内容 文件打开后可写可读, 默认在文件开头处写 |
| rb |
以二进制方式打文件 文件打开后只能读 |
| wb |
以二进制方式打开文件 文件打开后只能写 |
open函数需要三个参数,操作文件的名字,文件的模式,编码
成功打开文件后返回一个文件对象,我们可以用read和write方法来操作这个文件对象
同样还有一个close()方法来关闭文件
# 打开文件 f = open('testfile.txt', 'w+', encoding='gbk') f.write("你好") f.close()
# 读文件 f = open('testfile.txt', 'r+', encoding='gbk') print(f.read()) f.close()
如果我们要处理文件内容不是文本类型,比如音频,视频文件,软件,压缩包等等,打开文件指定的模式,就必须要用二进制方式打开
否则按默认的文本模式打开二进制文件,会出错,因为这种纯二进制的文件,根本找不到任何编码可以解析出字符串形式
# 打开二进制文件,实现复制mp3文件 fin = open('1.mp3', 'rb') fout = open('2.mp3', 'wb') fout.write(fin.read()) fout.close()
5.2 操作文件
open方法返回的文件对象,已经知道至少有三个方法,read,write,close
我们可以写一段代码,列出文件对象所有可以调用的方法
# 文件方法 f = open('testfile.txt', 'w+', encoding='gbk') l = [item for item in dir(f) if not item.startswith('_') and callable(getattr(f, item))] print(l)
['close', 'detach', 'fileno', 'flush', 'isatty', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']
# 读方法 f = open('诛仙.txt', 'r') print('read,按字符读取:%r'% f.read(3)) print('read,按字符读取:%r'% f.read(10)) print('readline,读取一行到列表中:%r' % f.readline()) print('readlines, 读取所有行到一个列表中:%r' %f.readlines()[:5]) f.close()
read,按字符读取:'时间:' read,按字符读取:'不明,应该在很早、很' readline,读取一行到列表中:'早以前。\n' readlines, 读取所有行到一个列表中:['地点:神州浩土。\n', '天地不仁,以万物为刍狗!\n', '这世间本是没有什么神仙的,但自太古以来,人类眼见周遭世界,诸般奇异之事,电闪雷鸣,又有天灾人祸,伤亡无数,哀鸿遍野,决非人力所能为,所能抵挡。遂以为九天之上,有诸般神灵,九幽之下,亦是阴魂归处,阎罗殿堂。\n', '于是神仙之说,流传于世。无数人类子民,诚心叩拜,向着自己臆想创造出的各种神明顶礼膜拜,祈福诉苦,香火鼎盛。\n', '自古以来,凡人无不有一死。但世人皆恶死爱生,更有地府阎罗之说,平添了几分苦惧,在此之下,遂有长生不死之说。\n']
# 写方法 f = open('testfile.txt', 'w+') f.write('春花秋月何时了,') f.write('往事知多少?\n') f.writelines(['小楼昨夜又东风,', '故国不堪回首月明中。']) f.close() f = open('testfile.txt', 'r') print(f.read())
春花秋月何时了,往事知多少?
小楼昨夜又东风,故国不堪回首月明中。
#迭代 fin = open('诛仙.txt', 'r') fout = open('诛仙2', 'w+') for line in fin: fout.write(line) fout.write('\n') fout.close() fin.close()
5.2.1 文件缓存
由于直接操作文件读写,相当于对计算机的硬盘进行操作,直接读写硬盘相比于读写内存来说,要慢上几万倍的时间
效率太低,所以操作系统给我们封装了一层,还刻操作系统的功能吗,操作系统直接控制底层的硬件,封装好之后提供给应用程序
硬盘就是计算机系统中的硬件,操作系统来对他们进行实际的读写,那我们在程序中写的读写程序,实际是在此之上进行的一种封装
操作系统在内存中给我们每个读写的文件分配一块空间,当我们读写文件的时候,实际上是在读写这一块内存空间,这样表面上看来我们是在读写文件
实际上是读写内存,这个效率一下就提高了,所以我们感觉读写文件好像挺快的
这块内存空间就叫文件的缓存。操作系统在适当的时候,会把缓存中的内容写到硬盘上去。
为什么我们操作文件完成后,必须要调用close()方法呢,在我们每次关闭文件的时候,操作系统就会把缓存中的内容,全部清空,并写到硬盘上去。
我们也可以调用 flush方法来完成强制刷新缓存,这个一般在我们写日志的时候,希望每句日志都能及时写在文件中,所以在写每一句后调用 flush方法强制把缓存中的内容写到硬盘中保存下来

5.2.2 二进制文件
文本文件使用read方法,读取的单位是字符,二进制的文件,读取的单位就是字节
比如有一个压缩文件太大了,我们想把他拆成几份,就可以使用二进制的方式打开,然后按字节读出,再写到若干个文件中去
# 二进制文件 SPLIT_MAX = 1000 * 1000 * 10 fin = open('python3.7.0.rar', 'rb') data = fin.read(SPLIT_MAX) i = 1 while data: fout = open('python_%s' % i , 'wb') fout.write(data) fout.close() i = i + 1 data = fin.read(SPLIT_MAX)
文本文件也可以用二进制的模式打开,只不过打开的就是纯字节码
以二进制模式打开的文件,不能指定文件的解码方式,因为不涉及文本,所以就不涉及编码
如果把文本文件以二进制字节的形式读出来后,想要查看文本内容就需要我们自己用字符串的decode方法来解码
# 二进制打开文本文件 fin = open('诛仙.txt', 'rb') data = fin.read(100 * 3) print(data.decode('utf-8'))
时间:不明,应该在很早、很早以前。
地点:神州浩土。
天地不仁,以万物为刍狗!
这世间本是没有什么神仙的,但自太古以来,人类眼见周遭世界,诸般奇异之事,电闪雷鸣,又有天灾人祸,伤亡无数,哀鸿遍野,决非人力
其实我们在访问文件的时候,需要有一个光标来指示当前操作文件的位置 ,比如读数据的话,就是从光标所在的位置开始读
写数据就是从光标所在的位置开始写
打开一个文件时,光标也类似偏移量从0开始,打开一个文件光标默认就在文件开始,光标的位置就是0
查看光标位置,我们可以通过 tell方法来获取文件光标的位置,光标随着我们读和写都会向后移动
#光标 fin = open('诛仙.txt', 'rb') print(fin.tell()) fin.read(10) print(fin.tell()) fin.close() fin = open("testfile.txt", 'w+') print(fin.tell()) fin.write('你好') print(fin.tell())
0 10 0 6
我们也可以自己移动光标的位置, 使用seek方法可以定位,定位分为相对定位 和绝对定位
通过seek的第二个参数来控制,0代表从文件头定位,1代表从当前位置定位 , 2代表从文件末尾
f = open("testfile.txt", 'w+') f.write('123456789\nABCDEF\n一二三四五六七\n') f.close() f = open('testfile.txt', 'rb') print(f.tell()) print(f.read(3)) print(f.tell()) f.seek(10, 0) print(f.read(10).decode('utf-8')) print(f.tell()) f.seek(-5, 1) print(f.tell()) print(f.read(5).decode('utf-8')) print(f.tell()) f.seek(-7, 2) print(f.tell()) print(f.read().decode('utf-8'))
seek定位的话必须用二进制方式打开
如果seek第二参数为0,从文件开头定位的话,可以用文本方式打开
f = open("testfile.txt", 'w+') f.write('123456789\nABCDEF\n一二三四五六七\n') f.close() f = open('testfile.txt', 'rb') print(f.tell()) print(f.read(3)) print(f.tell()) f.seek(10, 0) print(f.read(10).decode('utf-8')) print(f.tell()) f.seek(-5, 1) print(f.tell()) print(f.read(5).decode('utf-8')) print(f.tell()) f.seek(-7, 2) print(f.tell()) print(f.read().decode('utf-8'))
0 b'123' 3 ABCDEF 一 20 15 F 一 20 32 六七
5.3 标准文件
我们已经知道,操作系统来控制我们每一个计算机的硬件
那对于操作系统上的应用程序,操作系统提供了三个文件来代替我们计算机系统的输入和输出
这三个文件就叫标准 标准输入,标准输出,和标准错误
操作系统为每一个应用程序都提供了这三个文件 ,默认这三个文件 标准输入关联的是键盘
标准输出和标准错误关联的是显示器
在python中访问这三个访问,需要导入 sys模块
import sys sys.stdout.write('hello') sys.stdin.read(1) sys.stderr.write('error')

标准输入,标准输出,标准错误,默认是指我们的两个硬件,键盘和显示器
我们程序里input就是从键盘里读,print就是打印在显示器上
那我们同样可以自己打开一个文件对象,去替换stdin, stdout, stderr
这样之后,我们input就可以从文件里读,print就可以从文件里写
这种 替换标准对象的行为就叫重定向
# 重定向 import sys fin = open('testfile.txt', 'r') fout = open('output.txt', 'w') sys.stdin = fin sys.stdout = fout sys.stderr = fout s = input('>>>') print(s)
我们一定要区别文件,并不只是硬盘上保存的文件
一切IO都是文件,再简单点说,就是有read或write方法的对象就是文件对象
我们的标准IO可以重定向到任何一个文件对象上
比如我们可以用类,自己定义一个文件对象
import sys # 自定义文件对象 class MyFile: def __init__(self): self.buffer = '' def write(self, data): self.buffer += str(data) def flush(self): pass def readline(self): return '。。。' sin = MyFile() sout = MyFile() sys.stdin = sin sys.stdout = sout print('hello') s = input('>>>') print(s)
如果用debug模式运行程序,我们会发现重定向后,我们输入,输出都会对我们自定义的文件对象操作
我们定义的readline方法会被input调用,print时会调用我们的write方法,并且会补一个\n
一般重定向输出用的比较多,也就是说我们只实现一个write和flush方法就可以定义出一个可以重定向的文件对象了。

浙公网安备 33010602011771号