很多同事在文件操作这里出现问题,虽然会用,但原理不清晰,所以这里讲一下基础的文件处理。
一 文件操作介绍
计算机系统分为:硬件系统、软件系统两部分,软件系统又可以分为系统软件和应用软件。
我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件,但是,应用程序是无法直接操作硬件的,需要通过操作系统来完成。操作系统把复杂的硬件操作封装成简单的接口给用户/应用程序使用,其中文件就是操作系统提供给应用程序来操作硬盘虚拟概念,用户或应用程序通过操作文件,可以将自己的数据永久保存下来。
这样的话,我们只需要关注操作文件的流程就行了,也就是:
1.打开文件,得到文件句柄并赋值给一个变量
2.通过句柄对文件进行操作
3.关闭文件
二 python实现流程
根据上面的流程,在python中我们是这样实现的:
#1. 打开文件,得到文件句柄并赋值给一个变量
f=open('a.txt','r',encoding='utf-8') #默认打开模式就为r,也就是读文件
#2. 通过句柄对文件进行操作
data=f.read()
#3. 关闭文件
f.close()
现在我们可以分析一下第一步,操作系统和应用程序是怎么交互的:
1.由应用程序向操作系统发起系统调用open()
2.操作系统打开该文件,并返回一个文件句柄给应用程序
3.应用程序将文件句柄赋值给变量f
注意事项:
1.千万记得操作完文件后要写f.close()。从上面的交互流程我们可以看到,打开一个文件是包含两部分资源的:操作系统级打开的文件+应用程序的变量。在操作完毕一个文件时,必须把与该文件的这两部分资源一个不落地回收,回收方法为:
1、f.close() #回收操作系统级打开的文件
2、del f #回收应用程序级的变量
其中del f一定要发生在f.close()之后,否则就会导致操作系统打开的文件还没有关闭,白白占用资源,
而python自动的垃圾回收机制决定了我们无需考虑del f,这就要求我们,在操作完毕文件后,一定要记住f.close()
但还是经常有人会忘记加上,所以还有一种方式打开文件,并且不用你写close,就是通过with来管理上下文:
with open('a.txt','w') as f:
data = f.read()
with open('a.txt','r') as read_f,open('b.txt','w') as write_f:
data=read_f.read()
write_f.write(data)
#第二种格式通过逗号分开,可以在一行打开两个文件
2.建议指定编码。有时候大家打开文件,发现拿到的数据都是乱码的,这是因为f=open(...)是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明显是操作系统说了算了,操作系统会用自己的默认编码去打开文件,在windows下是gbk,在linux下是utf-8。若要保证不乱码,文件以什么方式存的,就要以什么方式打开。
f=open('a.txt','r',encoding='utf-8')
三 打开文件的模式
从上文我们知道:文件句柄 = open('文件路径', '模式'),模式可以是以下方式以及他们之间的组合:
| |
| |
Character | Meaning |
‘r' | open for reading (default) |
‘w' | open for writing, truncating the file first |
‘a' | open for writing, appending to the end of the file if it exists |
‘b' | binary mode |
‘t' | text mode (default) |
‘+' | open a disk file for updating (reading and writing) |
‘U' | universal newline mode (for backwards compatibility; should not be used in new code) |
我们逐个举例说明:
'r'模式,只读模式【默认模式,文件必须存在,不存在则抛出异常】,所以我们首先要有一个文件,我这里文件叫'陈粒',内容如下:
11111111
222222222
333
4444
555
555
6666
555
6666
'r'模式操作如下:
f=open('陈粒',encoding='utf-8')
data=f.read()
print(data)
f.close()
View Code
这样我们拿到的是文件所有的内容,那我们要一行行的读,就要用到readline()方法:
f=open('陈粒','r',encoding='utf-8')
print(f.readable())
print('第1行',f.readline())
print('第2行',f.readline())
print('第3行',f.readline())
View Code
结果是:
True
第1行
第2行
第3行
我们用了3个readline(),读取的是前3行的内容,说明readline()在读取一行内容后,光标移动到下一行首部。光标是什么,我们写文件的时候,一直会有一个光标在最后闪烁,我们读取文件的时候,是从光标的位置开始读的(光标是默认在0位置上的,后面会讲一个移动光标的方法叫seek)。
如果我想要里面任意一行,比如最后一行的内容,上面的方法就很难做到了,这时候需要用到readlines()方法:
f=open('陈粒','r',encoding='utf-8')
data=f.readlines()
print(data)
print(data[-1])
f.close()
View Code
结果是:
['11111111\n', '222222222\n', '333\n', '4444\n', '555\n', '555\n', '6666\n', '555\n', '6666\n']
6666
可以看到readlines()拿到的是所有结果,但是按行放在一个列表里面,通过列表索引,我们可以拿到任何一行的内容。
'w'模式:只写模式【不可读;不存在则创建;存在则清空内容】,可以自己创建,那就简单了,直接来:
f=open('陈粒1','w',encoding='utf8')
f.write('11111111\n')
f.write('222222222\n')
f.write('333\n4444\n555\n')
f.close()
View Code
运行之后你会发现,多了一个文件叫“陈粒1”,里面就是我们写入的内容:
11111111
222222222
333
4444
555
可见我们可以写一行,加上换行符就可以写多行,那除了write()方法,还有别的方法可以写吗?当然是有的,比如writelines(),和我们的readlines()感觉对应,所以里面的内容也要是一个列表:
f=open('陈粒1','w',encoding='utf8')
f.write('11111111\n')
f.write('222222222\n')
f.write('333\n4444\n555\n')
f.writelines(['555\n','6666\n'])
f.close()
View Code
再看我们的文件,又多了两行。下面再看一段代码:
f=open('陈粒1','w',encoding='utf8')
f.write('11111111\n')
f.write('222222222\n')
f.write('333\n4444\n555\n')
f.writelines(['555\n','6666\n'])
f.writelines(['555\n','6666\n',1]) # 文件内容只能是字符串,只能写字符串
f.close()
View Code
运行之后发现报错了,因为我们最后写入了一个数字,而文件的内容都是字符串,所以写也都是写入字符串的,不能写数字,当然其他类型也是不可以的。
'a'追加写模式【不可读;不存在则创建;存在则只追加内容】,看到我们上面的w模式,写在同一个文件里面,会把之前的覆盖,那么如果只想在文件里面加点东西,显然需要另一种模式,就是a模式:
f=open('陈粒1','a',encoding='utf-8')
f.write('写到文件最后')
View Code
这时候看结果,最后已经加上一行了:
11111111
222222222
333
4444
555
555
6666
555
6666
写到文件最后
上面说的3种模式非常简单,也是我们常用的3种模式,但都只是单一的模式,很多时候我们需要同时对一个文件进行读写操作,就需要这三个配合'+'一起使用了:
"+" 表示可以同时读写某个文件
r+, 读写【可读,可写】
w+,写读【可读,可写】
a+, 写读【可读,可写】
文件没有修改这一说,大家所认知的修改文件,是应用程序为了方便使用写的一个功能,改是在内存中改,但最终还是覆盖硬盘中的源文件,所以修改文件本质上是覆盖之前的文件。所以我们看看'r+'模式:
f = open('陈粒1','r+',encoding='utf-8')
data = f.read()
print(data)
# f.write('aaa')
f.close()
View Code
从结果看到,读文件是没有问题的,现在把写的那一行取消注释,看看文件内容:
11111111
222222222
333
4444
555
555
6666
555
6666
写到文件最后aaa
不会换行写,如果你要换行写,就要在aaa前面加上\n,那单独写是什么情况:
f = open('陈粒1','r+',encoding='utf-8')
# data = f.read()
# print(data)
f.write('aaa')
# f.write('\naaa')
f.close()
View Code
结果是:
aaa11111
222222222
333
4444
555
555
6666
555
6666
写到文件最后
覆盖了前面三个111,因为光标默认是0,而写是从光标处开始写的,所以导致覆盖,这几种模式理解就行,用到不多。那如果我们要修改文件怎么办?就要打开两个文件了:
r_f = open('陈粒1','r',encoding='utf-8')
data = r_f.readlines()
r_f.close()
w_f = open('陈粒2','w',encoding='utf-8')
data.pop(2)
w_f.writelines(data)
w_f.close()
View Code
一个文件读,一个文件写,写之前把读到的数据列表删除了一个元素,所以’陈粒2‘文件就少了一行333,这样就实现了文件修改啦。有人说这样拿到的是新文件,但我想要在原来的文件修改怎么办?那就在改完之后把源文件删除后,再把新文件rename一下就好了,os模块能搞定这个需求。下一篇要讲的是二进制方式打开文件。