首先,我们需要知道一个概念:应用程序是不能直接对电脑硬件进行操作的
在操作系统中,操作系统对外提供了文件系统,硬盘上的文件都由文件系统进行资源管理,
读写硬盘是一种硬件操作,所以我们要想进行文件操作,就必须通过文件系统这个接口来进行文件操作
因此
我们要想进行文件读写等操作,就必须先向操作系统发起系统调用,
由操作系统的内核来进行文件的读写操作,
操作系统把执行结果返回给应用程序,
最后则应用程序把执行结果呈现在用户面前
python提供了一个进行文件读写操作的系统调用方法:open()
open()是python内置函数,我们可以通过调用open()函数来向操作系统发起系统调用,进行文件的读写操作
open()函数的用法
新建一个open_func.py文件,再新建一个file1.txt文件
file1.txt文件内容为:
离离原上草,
一岁一枯荣。
野火烧不尽,
春风吹又生。
先在open_func.py文件中打开file1.txt
>>> f=open("file1.txt") # 没有指定file1.txt文件的绝对路径,所以这里用的是相对路径
# 第二个参数为读取文件的模式,不写默认为"r",即读取模式
# 把打开文件的句柄赋值给一个变量f
>>> print(f)
<_io.TextIOWrapper name=‘file1.txt‘ mode=‘r‘ encoding=‘UTF-8‘>
>>> f.read() # f.read()会读取指定文件的指定长度字符的内容,如果没有指定读取长度,则读取所有内容
‘离离原上草,\n一岁一枯荣。\n野火烧不尽,\n春风吹又生。\n‘
>>> f.read() # 再次调用f.read()方法,其打印结果为空,也说明上一次f.read()读取指定文件的所有内容
‘‘
>>> f.close() # 关闭文件
需要注意的点:
- 打开一个文件操作完成后一定要关闭这个文件,否则被打开的文件会一直存在于系统内存中,占用系统资源
指定读取长度的方法:f.read()
[[email protected] ~]# cat file2.txt # 在系统命令提示符下查看文件file2.txt的内容
python
linux
mysql
>>> f=open("file2.txt") # 以默认方式打开一个不存在的文件file2.txt,会抛出异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: ‘file2.txt‘
在python解释器中进行操作
>>> f=open("file2.txt","r") # 以读模式打开文件
>>> f.read(2) # 读取两个字符
‘py‘
>>> f.read(5) # 再向后读取5个字符
‘thon\n‘
>>> f.read(5) # 再次继续读取5个字符
‘linux‘
>>> f.close() # 关闭文件
一次读取一行内容的方法:f.readline()
>>> f=open("file1.txt") # 再次打开file1.txt文件
>>> f.readline() # f.readline()方法执行一次读取文件的一行内容
‘离离原上草,\n‘
>>> f.readline() # 再次执行f.readline()方法,会接着光标在文件中的内容再次读取文件的一行内容
‘一岁一枯荣。\n‘
>>> print(f.readline(),end="") # 这次使用print()方法打印f.readline()方法读取的一行内容,并指定文件结尾
野火烧不尽,
>>> print(f.readline(),end="")
春风吹又生。
>>> f.readline() # 文件已经被读取完了,再次调用f.readline()方法读取文件内容会返回空
‘‘
>>> f.close() # 因为文件已经被读取完,光标已经被移动到文件的末尾了,所以关闭文件
读取整个文件内容生成列表的方法:f.readlines()
>>> f=open("file1.txt") # 打开文件
>>> f.readlines() # f.readlines()会一次性读取文件的所有内容,并把每一行做为一个元素放入一个列表中
[‘离离原上草,\n‘, ‘一岁一枯荣。\n‘, ‘野火烧不尽,\n‘, ‘春风吹又生。\n‘]
>>> f.write("aaa") # 向文件中写入一行"aaa",因为是以读的方式打开文件,所以调用f.write()方法向文件中写入一行是会提示错误:无法写入
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
io.UnsupportedOperation: not writable
>>> f.close()
现在再以"w"的方式打开文件
>>> f=open("file1.txt","w") # 以"w"的方式打开文件
>>> f.close() # 关闭文件
重新打开一个命令窗口,查看file1.txt文件的内容
在前面的例子里,file1.txt文件里是有内容的,但是在这里查看文件内容却是空了
[[email protected] ~]# cat file1.txt # 可以看到文件中没有任何内容
[[email protected] ~]# ll file1.txt # 文件大小变为0了
-rw-r--r-- 1 root root 0 Jan 27 22:46 file1.txt
在上面的例子里,我们可以看到,以"w"的方式打开文件时,会清除指定文件原有的内容
实际上,在python中,以"r"方式打开文件时,如果被打开的文件不存在时,是会抛出异常的
而以"w"模式打开文件时,如果文件不存在则新建这个文件,如果文件已经存在,则清空这个文件的原有内容
再看下一个例子
>>> f=open("file1.txt","w") # 以"w"模式打开file1.txt
>>> f.write("hello world") # 向文件中写入一行内容
11
>>> f.write("hello python") # 向文件中再次写入一行内容
12
>>> f.close() # 关闭文件
在系统命令行中查看file1.txt文件的内容及大小
[[email protected] ~]# cat file1.txt # 查看文件内容
hello worldhello python[[email protected] ~]# ll file1.txt
-rw-r--r-- 1 root root 23 Jan 27 22:59 file1.txt
查看文件内容可以看到文件中没有换行符,所以在向文件中写入内容时,需要我们自己在末尾处添加换行符
>>> f=open("file1.txt","w") # 以"w"模式打开file1.txt
>>> f.write("hello python\n") # 向文件中添加一行,在行尾添加换行符
13
>>> f.write("hello world\n") # 再次向文件中添加一行,在行尾也添加换行衔
12
>>> f.close() # 关闭文件
再次在系统命令提示符中查看文件内容及大小
[[email protected] ~]# cat file1.txt # 查看文件内容,可以看到现在已经可以正常显示了
hello python
hello world
[[email protected] ~]# ll file1.txt # 查看文件大小
-rw-r--r-- 1 root root 25 Jan 27 23:03 file1.txt
在对文件进行读操作时,f.readlines会一次性读取文件的所有内容,并把每一行做为一个元素添加到列表中
在对文件进行写操作时,也有一个f.writelines方法.
把一个以字符串为元素的列表写入到文件中的方法:f.writelines()
f.writelines()方法可以把一个列表的元素做为行写入到文件中
需要注意的是:列表中的元素必须是字符串类型
>>> f=open("file1.txt","w")
>>> f.writelines(["python\n","linux\n","mysql\n"]) # 调用f.writelines()方法把一个列表写入到一个文件中
>>> f.close()
在系统命令提示符窗口中查看文件内容及大小
[[email protected] ~]# cat file1.txt # 查看文件内容
python
linux
mysql
[[email protected] ~]# ll file1.txt
-rw-r--r-- 1 root root 19 Jan 27 23:09 file1.txt
可以看到字符串元素的列表已经被写入到文件中去了。
以"a"模式打开文件并进行操作
以"a"模式打开文件时,如果指定文件不存在,则创建指定文件
如果指定文件已经存在,则把光标移动到文件末尾,在文件最后一行进行添加内容操作
>>> f=open("file1.txt","a") # 以"a"模式打开文件
>>> f.write("nginx\n") # 向文件末尾追加一行内容
6
>>> f.write("javascript\n") # 向文件末尾再次追加一行内容
11
>>> f.close() # 关闭文件
在系统命令提示符窗口中查看文件内容及大小
[[email protected] ~]# cat file1.txt # 可以看到文件file1.txt的末尾已经添加了两行内容
python
linux
mysql
nginx
javascript
[[email protected] ~]# ll file1.txt # 文件大小发生改变
-rw-r--r-- 1 root root 36 Jan 27 23:16 file1.txt
注意点:
在向文件中添加内容时,实际上是在内存中向文件添加内容的,只有等到文件被关闭的时候,解释器才会把内存中被修改的文件内容同步到文件中,在这个期间是有风险的,比如在文件未被保存到硬盘上之前,主机突然断电,那么这个文件的内容就有可以被损坏
这时可以使用f.flush()方法把内存中被修改的文件内容强行保存到硬盘中
手动把内存中的文件保存到硬盘上的方法:f.flush()
>>> f=open("file1.txt","a") # 以追加模式打开文件
>>> f.write("mongodb\n") # 向文件中添加一行内容
8
>>> f.write("openstack\n") # 再次向文件中添加一行内容
10
>>> f.flush() # 调用f.flush()方法,把内存中的文件内容保存到硬盘中
在系统命令提示符窗口中查看文件内容及大小
[[email protected] ~]# cat file1.txt # 在前面,并没有关闭文件,在这里还是可以看到添加后的文件内容
python
linux
mysql
nginx
javascript
mongodb
openstack
[[email protected] ~]# ll file1.txt # 文件大小发生改变
-rw-r--r-- 1 root root 54 Jan 28 10:57 file1.txt
f.closed查看文件是否关闭,返回一个布尔值
这个例子接着上面的例子进行操作
>>> f.closed # 在上面的例子里,调用f.flush()方法把内存中的文件内容保存到硬盘上,并没有关闭文件,查看文件是否关闭,返回False,说明文件没有关闭
False
>>> f.close() # 关闭文件
>>> f.closed # 再次查看文件是否关闭,返回True说明文件已经关闭了
True
把光标移动到文件指定位置的方法:f.seek(offset[,whence])
参数说明:
第一个参数为移动的字节数,第二个参数表示从哪个位置开始移动,
0表示以文件开始为参照点
1表示以当前位置为参数点
2表示以文件末尾位置为参照点
例子:
>>> f=open("file1.txt","rb") # 在这里必须以"b"模式打开文件,否则会抛出异常
>>> f.seek(5) # 当前光标在文件首部,把光标从文件首部向后移动5个字节
5
>>> f.seek(5,1) # 再以光标当前所有位置为参照,向后移动五个字节
10
>>> f.tell() # 查看当前光标所在位置
10
>>> f.seek(3,1) # 以光标当前所在位置为参照,向后移动3个字节
13
>>> f.tell() # 查看当前光标所有位置
13
>>> f.seek(-2,2) # 把光标移动到文件末尾,然后再向前移动2个字节
52
>>> f.read() # 读取光标所在处到文件末尾的所有内容
b‘k\n‘
>>> f.tell() # 查看光标所在位置
54
>>> f.close()
查看当前光标所在位置的方法:f.tell()
f.tell()方法的位置为字节,返回值为数字
例子:
>>> f=open("file1.txt","r")
>>> f.read()
‘python\nlinux\nmysql\nnginx\njavascript\nmongodb\nopenstack\n‘
>>> f.seek(10)
10
>>> f.tell()
10
>>> f.read()
‘ux\nmysql\nnginx\njavascript\nmongodb\nopenstack\n‘
>>> f.tell()
54
以光标所在位置为参照,截取指定长度的内容,其余内容被删除:f.truncate()
例子:
>>> f=open("file2.txt","w") # 以写模式打开文件
>>> f.write("1111111\n") # 向文件中写入一行内容
8
>>> f.write("2222222\n") # 向文件中写入第二行内容
8
>>> f.write("3333333\n") # 向文件中写入第三行内容
8
>>> f.truncate(5) # 从文件开始位置向后截取5个字节的长度的内容
5
>>> f.close()
在系统命令提示符下查看文件内容
[[email protected] ~]# cat file2.txt
11111[[email protected] ~]#
其余的用法
f.name # 获取被打开文件的文件名
f.encoding # 获取文件的被打开的编码方式
f.readable() # 判断文件当前是否可读
f.writable() # 判断文件当前是否可写
例子:
>>> f=open("file1.txt","r") # 以读模式打开文件file1.txt
>>> f.name # 查看文件名
‘file1.txt‘
>>> f.encoding # 查看文件以哪种编码方式被打开
‘UTF-8‘
>>> f.readable() # 以读模式打开此文件,所以判断文件是否可读,返回值为True
True
>>> f.writable() # 以读模式打开此文件,所以判断文件是否可写,返回值为False
False
>>> f.close() # 关闭文件
>>> f=open("file2.txt","w") # 以写模式打开文件file2.txt
>>> f.name
‘file2.txt‘
>>> f.encoding
‘UTF-8‘
>>> f.readable() # 以写模式打开文件,所以在这里判断文件是否可读时,返回False
False
>>> f.writable() # 以写模式打开文件,判断文件可写返回值为True
True
>>> f.close()
在python中除了上面说的三种打开文件的办法外,还有另外一种打开文件的方法,那就是以二进制模式打开文件,即"rb","wb","ab"
现有另一个文件file2.txt
,内容为
离离原上草,
一岁一枯荣。
野火烧不尽,
春风吹又生。
以"rb"
模式打开文件file2.txt
,查看读取到的内容
>>> f=open("file2.txt","rb") # 以读模式打开文件file2.txt
>>> f.read() # 查看文件内容时,可以看到是bytes格式的
b‘\xe7\xa6\xbb\xe7\xa6\xbb\xe5\x8e\x9f\xe4\xb8\x8a\xe8\x8d\x89\xef\xbc\x8c\n\xe4\xb8\x80\xe5\xb2\x81\xe4\xb8\x80\xe6\x9e\xaf\xe8\x8d\xa3\xe3\x80\x82\n\xe9\x87\x8e\xe7\x81\xab\xe7\x83\xa7\xe4\xb8\x8d\xe5\xb0\xbd\xef\xbc\x8c\n\xe6\x98\xa5\xe9\xa3\x8e\xe5\x90\xb9\xe5\x8f\x88\xe7\x94\x9f\xe3\x80\x82\n‘
>>> f=open("file2.txt","rb")
>>> f.read().decode("utf8") # 因为写入时是以"utf8"编码格式写入的,所以在这里要解码成"utf8"格式的
‘离离原上草,\n一岁一枯荣。\n野火烧不尽,\n春风吹又生。\n‘
>>> f=open("file3.txt","wb") # 以写模式打开文件file3.txt
>>> f.write("python") # 此时直接向f对象中写入内容会抛出异常:写入的内容需要是bytes对象,不能是字符串格式的
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a bytes-like object is required, not ‘str‘
>>> f.write("python\n".encode("utf8")) # 编码成‘utf8‘格式,再写入
7
>>> f.write("你好呀\n".encode("utf8"))
10
>>> f.close()
在系统命令提示符下查看文件大小及内容
[[email protected] ~]# cat file3.txt
python
你好呀
[[email protected] ~]# ll file3.txt
-rw-r--r-- 1 root root 17 Jan 28 16:41 file3.txt
以"b"模式打开文件的好处:
1.读取文件的时候就是二进制格式的,不容易产生乱码
2.处理非文本类型的文件时,写入文件的时候就需要以二制格式写入,否则会抛出异常
例子:
用python的二进制方法把一个张图片aa.jpg复制一份bb.jpg
read_file=open(‘aa.jpg‘,‘rb‘)
write_file=open("bb.jpg","wb")
write_file.write(read_file.read())
[[email protected] ~]# ll *.jpg
-rw-r--r-- 1 root root 95406 Jan 19 08:38 aa.jpg
-rw-r--r-- 1 root root 95406 Jan 28 17:15 bb.jpg
总结:
打开文件时,需要指定文件路径和打开方式。打开文件后,即可获取指定文件的文件句柄,然后通过操作文件句柄来操作指定文件
打开文件的模式有:
r 只读模式,打开文件默认模式,文件必须存在,不存在则抛出异常
w 只写模式,文件不存在则创建,存在则清空原有内容
a 追加模式,可以读取文件,文件不存在则创建,存在则向文件追加内容
“+”表示可以同时读写某个文件
r+ 读写(可读可写)
w+ 写读(可读可写)
a+ 写读(可读可写)
"b"表示以字节的方式操作
rb
wb
ab
以b方式打开文件时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码
小demo:
用python方法,替换文件中的内容
查看初始文件内容
[[email protected] ~]# cat test.txt
aaaapythonaaaapythonaa
bbbblinuxbbblinuxbbb
cccmysqlcccmysqlccc
dddnginxdddnginxddd
现在想打test.txt文件中所有的nginx换成httpd
脚本如下:
import os
read_f = open("test.txt", "r")
write_f = open(".text.txt.swap", "w")
for line in read_f:
if ‘nginx‘ in line:
line = line.replace("nginx", "httpd")
write_f.write(line)
read_f.close()
write_f.close()
os.remove("test.txt")
os.rename(".text.txt.swap", "test.txt")
运行这个python脚本,然后查看test.txt的内容
[[email protected] ~]# cat test.txt
aaaapythonaaaapythonaa
bbbblinuxbbblinuxbbb
cccmysqlcccmysqlccc
dddhttpddddhttpdddd
会发现文件test.txt的内容已经被修改了
with上下文管理
使用open打开文件时,很容易会忘记关闭被打开的文件,这要一来被打开的文件一直运行在内存中,无形中造成了系统资源的浪费
这时可以使用with上下文管理来进行文件操作,以解决因为忘记关闭文件而造成的系统资源的浪费
使用with重构上面的内容替换的例子
import os
with open("a.txt","r") as read_f,open(".a.txt.swap","w") as write_f:
for line in read_f:
if "nginx" in line:
line=line.replace("nginx","httpd")
write_f.write(line)
os.remove("a.txt")
os.rename(".a.txt.swap","a.txt")
也可以完成同样的功能,而且不用手动关闭被打开的文件