1. 学前三问
1.1 什么是文件 ?
文件是操作系统提供给用户/应用程序操作硬盘的一种虚拟的概念/接口。
用户/应用程序
操作系统(文件) 操作系统会把对用户/应用程序文件功能的调用转成具体的硬盘操作
计算机硬件(硬盘)
1.2 为何要用文件
用户/应用程序可以通过文件将数据永久保存的硬盘中,即操作文件就是操作硬盘。
用户/应用程序直接操作的是文件,对文件进行的所有操作,都是在向操作系统发送系统调用,然后再由操作将其
转换成具体的硬盘操作。
1.3 如何用文件:open( )
1.3.1 控制文件读写内容的模式:t和b
强调: t和b不能单独使用,必须跟r/w/a连用,默认就是t, t代表text,通常不写
t文本(默认的模式)
- 读写都以str(unicode)为单位
- 文本文件
- 必须指定encoding='utf-8'
b二进制/bytes
- 一般是一些音频文件或者视频文件
1.3.2 控制文件读写操作的模式
- r 只读模式
- w 只写模式
- a 只追加写模式
- +: r+, w+, a+ 在原来模式的基础上只读的变可读可写,可写的变可写可读
2. 文件操作的基本流程
2.1 打开文件
open("文件路径")
open("C:acd.txt") # 但是(右斜杠)在字符串中转义的作用
# 如果文件夹叫nb,那写成路径
b在字符串中就是换行符的意思了
2.1.1 如何取消转义?
- 字符串前+r,eg:
r"C:a bd.txt"
- 用左斜杠代替右斜杠,open功能自动会把左斜杠替换成右斜杠
2.1.2 路径分割符
- win下默认的路径分隔符是
右斜杠
- linux下默认的路径分隔符是
左斜杠/
2.1.3 绝对路径和相对路径
绝对路径就是文件的全部路径,从开始的盘符一直写到该文件。
open(r'E:projectpythons29codeday111.打开文件.py')
相对路径就是以当前文件所在的文件夹为基础,然后找文件,所以也只能找到同文件夹下的文件
2.2 操作文件
# 1. 打开文件,由应用程序向操作系统发起系统调用open(...),
# 操作系统打开该文件,对应一块硬盘空间,并返回一个文件对象赋值给一个变量f
f=open('a.txt',moder='r',encoding='utf-8') #默认打开模式就为r
# 2. 调用文件对象的读/写方法,会被操作系统转换为读/写硬盘的操作
data=f.read() # 读取文件内容,将硬盘中的二进制数据读取到内存中 --> t控制将二进制转换成字符
2.3 关闭文件
打开一个文件包含两部分资源:应用程序的变量f和操作系统打开的文件。在操作完毕一个文件时,必须把与该文
件的这两部分资源全部回收,回收方法为:
1、f.close() #回收操作系统打开的文件资源,一旦关闭就不能操作了,但是f变量还在
2、del f #回收应用程序级的变量,一般我们不用写。
其中del f一定要发生在f.close( ) 之后,否则就会导致操作系统打开的文件无法关闭,白白占用资源, 而python
自动的垃圾回收机制决定了我们无需考虑del f,这就要求我们,在操作完毕文件后,一定要记住f.close( )。
2.4 with上下文管理器
with上文帮你打开文件,下文就是帮你自动关闭文件.
with open('a.txt',mode='rt') as f1:
res = f1.read() # 读取文件内容,硬盘的搬运工
print(res)
# f1称之为文件对象/文件句柄,就是用来控制文件的
# 当with语句的代码块执行完毕后会自动执行f.close()
# 打开多个文件
with open('a.txt',mode='rt') as f1,open('b.txt',mode='rt') as f2:
#with open('a.txt',mode='rt') as f1,
# open('b.txt',mode='rt') as f2: 通过右斜杠将一句代码换行写
res1 = f1.read() # 读取文件内容
res2 = f2.read()
print(res1)
print(res2)
2.5 指定操作文件的字符编码
open功能还有一个参数就是encoding='解码格式'
with open(r'a.txt', mode='rt',encoding='utf-8') as f1:
res = f1.read() # t模式会将f.read()读出来的结果解码成unicode
print(res, type(res))
# 内存: utf-8格式的二进制 ----> 解码 ----> unicode
# 硬盘(a.txt内容: utf-8格式的二进制)
如果你不指定encoding参数的话,操作系统会使用自己的默认编码进行解码。
- mac/linux utf-8
- windows gbk
3. 文件的操作模式
3.1 控制文件操作的三种模式
r(默认的):只读
w:只写
a:只追加写
3.1.1 r 模式的使用
r (默认的操作模式):只读模式,当文件不存在时,会报错。
with open(r'd.txt', mode='rt',encoding='utf-8') as f1:
res = f1.read()
print(res, type(res))
# 运行结果:
Traceback (most recent call last):
File "E:/project/python/s29code/day11/2.r模式.py", line 1, in <module>
with open(r'd.txt', mode='rt',encoding='utf-8') as f1:
FileNotFoundError: [Errno 2] No such file or directory: 'd.txt'
当文件存在时,文件指针跳到开始位置,所以当我们f.read()
时会把文件指针从头读到尾,即一次性
把文件内容全部从硬盘加载到内存中。注意:大文件不能这样读,如果内容过大,会撑爆内存
# a.txt
你好啊
with open(r'a.txt', mode='rt',encoding='utf-8') as f1:
res = f1.read() # 把所有内容从硬盘读入内存
print(res) # 你好啊
3.1.2 w模式的使用
w模式,是只写模式,当文件不存在的时候,用w模式打开的话,会自动在该路径创建一个新的文件。
当文件存在时,w模式会先清空原来的内容,指针位于开始位置,然后再写内容。
with open(r'e.txt', mode='w',encoding='utf-8') as f1:
f1.write("你好")
注意:w模式不是覆盖,可以自己尝试一下,假设原文件的内容很多,你用w模式打开,然后只写一个字符
试试。
但是如果我们用w模式没有关闭,连续写入内容的话,不会清空之前的内容,并紧跟在旧的内容后面。
with open('b.txt',mode='w',encoding='utf-8') as f:
f.write('大笨蛋
')
f.write("大傻子
")
f.write("二杆子")
# b.txt
大笨蛋
大傻子
二杆子
3.1.3 a模式的使用
a模式是只追加写模式,当文件不存在时,和w模式一样,会创建新文件。
当文件存在的时候,a模式打开文件,不会清空原文件的内容,指针会自动移到文件末尾。
with open('c.txt',mode="a",encoding="utf-8") as f:
f.write('我是第一行
')
# c.txt
我是第一行
当文件没有关闭的情况,用a模式持续写入的话,是和w模式一样的,新内容会紧跟旧内容的后面
with open('c.txt',mode="a",encoding="utf-8") as f:
f.write('我是第一行
')
f.write('我是第二行
')
f.write('我是第三行
')
f.write('我是第四行
')
# c.txt
我是第一行
我是第二行
我是第三行
我是第四行
a模式和w模式的异同点:
- 相同点:只能写,不能读,当文件没有关闭时,连续写入,新内容紧跟在就内容之后
- 不同点:a模式打开已存在的文件,不会清空原文件内容,指针自动移动到文件末尾
w模式和a模式的应用场景:
-
w模式一般用于新文件的保存,比如copy文件底层就是把一个文件读出来,然后用w写入到新文件
-
a模式则是适用于在原文件的基础上写新内容,比如记录日志,用户注册记录文件
3.1.4 了解+模式
+模式,不能单独使用,只能和r,w或a模式一起使用
- r+ 读写模式,基准是r模式,当文件不存在时,用r+打开会报错,指针在开头。
- w+ 写读模式,基准是w模式,文件不存在会创建文件,文件存在同样会清空原文件
- a+ 追加读模式,基准是a模式,文件不存在会创建文件,指针在末尾
3.1.5 提一嘴x模式
x模式,只写模式,当文件不存在的时候会创建新文件,指针在文件开头,但是当文件存在的时候会直接报错
# d.txt 已经存在的文件
with open('d.txt',moder="x",encoding="utf-8") as f:
f.write("你好")
Traceback (most recent call last):
File "E:/project/python/s29code/day11/5.x模式.py", line 1, in <module>
with open('d.txt',mode='x',encoding='utf-8') as f:
FileExistsError: [Errno 17] File exists: 'd.txt'
3.2 控制文件内容的两种模式
大前提: tb模式均不能单独使用,必须与r/w/a之一结合使用
t(默认的):文本模式
- 读写文件都是以字符串( unicode )为单位的
- 只能针对文本文件
- 必须指定encoding参数
b:二进制模式:
- 读写文件都是以bytes/二进制为单位的
- 可以针对所有文件
- 一定不能指定encoding参数
3.2.1 t 模式
# t 模式:如果我们指定的文件打开模式为r/w/a,其实默认就是rt/wt/at
with open('a.txt',mode='rt',encoding='utf-8') as f:
res=f.read()
print(type(res)) # 输出结果为:<class 'str'>
with open('a.txt',mode='wt',encoding='utf-8') as f:
s='abc'
f.write(s) # 写入的也必须是字符串类型
#强调:t 模式只能用于操作文本文件,无论读写,都应该以字符串为单位,而存取硬盘本质都是二进制的形式,当指定 t 模式时,内部帮我们做了编码与解码
# 如果我们用t模式打开视频文件的话
with open('a.mp4',mode='rt',encoding='utf-8') as f:
... # ... == pass 这样并不会报错,因为我们只是调用了操作系统的接口,告诉他用rt打开文件
# 以后用utf-8对读入内存的文件进行解码,但是我们还没有读/写文件,只是打开文件,所以不报错
3.2.2 b模式
同样b模式也是不能单独使用的,必须和r、w、a配合使用,t模式只针对字符文件,而b模式是针对所有文件,包
包括文本文件,但是一定要注意不能指定encoding参数,否则报错。
with open('a.jpg',mode='rb') as f:
res = f.read() # 硬盘的二进制读入内存->b模式下,不做任何转换,直接读入内存
print(res,type(res)) # bytes类型 -> 当成二进制
# b'xffxd8xffxe0x00x10JFIFx00x01x01x00x00x01x00x01x00x0C
# <class 'bytes'>
# 理论上我们应该读出来的是物理层间的010101这种,但是如果以这种显示的话,不利于阅读,以及操作数据
# 所以python对读出来的内容做了转换,是16进制的显示,平常我们就把当成二进制来看就行了.
with open('b.txt',mode='rb') as f:
res = f.read() # 把躺在硬盘的二进制直接读出来,不做任何转换
print(res,type(res))
print(res.decode('utf-8')) # 文件是以utf-8编码格式存的,可以进行字符解码
4. 循环读取文件的两种方式
4.1 for循环一行一行的读
for循环不但可以循环那些基础数据类型,也可以循环文件句柄, 每次循环,以行为单位
但是for循环有个局限
# t模式
with open('b.txt',mode='r',encoding='utf-8') as f:
for line in f:
print(line)
# 打印内容:
# 是可拨打发红包测
# 我付款金额日本
# 费劲儿家人
# b模式
with open('b.txt',mode='rb') as f:
for line in f:
print(line)
# 打印内容:
b'xe6x98xafxe5x8fxafxe6x8bxa8xe6x89x93xe5x8fx91xe7xbaxa2xe5x8cx85xe6xb5x8b
' #
--> 换行符
b'xe6x88x91xe4xbbx98xe6xacxbexe9x87x91xe9xa2x9dxe6x97xa5xe6x9cxac
'
b'xe8xb4xb9xe5x8axb2xe5x84xbfxe5xaexb6xe4xbaxba
'
b'xe4xbbx98xe9x87x91xe9xa2x9dxe6x97xa5xe5x93xa6xe5x9bx9exe9xa1xbe'
4.2 while循环
之前就说过for循环能实现的,while循环也能实现,循环读取文件也是可以的
# t模式和b模式都适用
with open('b.txt',mode='rb') as f:
while 1:
content = f.read(1024) # 可以指定读取文件内容的大小,以字节为单位
if not content:
break
print(content)
4.3 小练习
文件拷贝小程序,要求:字符文件和文本文件都适用。5.
file_path = input('请输入要拷贝的文件的绝对路径:')
new_file_path = input('请输入新文件的绝对路径:')
with open(file_path, mode='rb') as f,
open(new_file_path, mode='wb') as f1:
for line in f:
f1.write(line)
5. f 的常用方法
5.1 必须掌握
# 读操作
f.read() # 读取所有内容,执行完该操作后,文件指针会移动到文件末尾
f.readline() # 读取一行内容,光标移动到第二行首部
f.readlines() # 读取每一行内容,作为一个元素存放于列表中
# 强调:
# f.read()与f.readlines()都是将内容一次性读入内容,如果内容过大会导致内存溢出,若还想将内容全读入内存,则必须分多次读入,有两种实现方式:
# 方式一
with open('a.txt',mode='rt',encoding='utf-8') as f:
for line in f:
print(line) # 同一时刻只读入一行内容到内存中
# 方式二
with open('1.mp4',mode='rb') as f:
while True:
data=f.read(1024) # 同一时刻只读入1024个Bytes到内存中
if len(data) == 0:
break
print(data)
# 写操作
f.write('1111
222
') # 针对文本模式的写,需要自己写换行符
f.write('1111
222
'.encode('utf-8')) # 针对b模式的写,需要自己写换行符
f.writelines(['333
','444
']) # 迭代的将列表的元素写入文件
f.writelines([bytes('333
',encoding='utf-8'),'444
'.encode('utf-8')]) #b模式
5.2 了解操作
f.readable() # 文件是否可读
f.writable() # 文件是否可读
f.closed # 文件是否关闭
f.encoding # 如果文件打开模式为b,则没有该属性
f.flush() # 立刻将文件内容从内存刷到硬盘,一般情况下当所有写操作完毕后,才会写入硬盘,但是我们 # 可以用flush强制写到硬盘
f.name # 返回文件名称
6. 文件指针的移动
f.seek(n,模式) n指的是移动的字节个数
f.tell( ) 返回文件指针当前位置
指针移动的单位都是以字节(bytes)为单位
只有一种情况特殊:
t模式下的read(n) n代表字符的个数强调: t模式下只能用0模式,b模式下0,1,2都可以用
6.1三种模式
模式:
0:参照物是开头位置
1:参照物是当前位置
2:参照物是文件末尾
6.1.1 0模式
with open(r'a.txt', mode='rt', encoding='utf-8') as f:
f.seek(6, 0)
f.seek(3,0)
# f.seek(3, 1) # 报错
res = f.read()
print(res)
6.1.2 1模式
with open(r'a.txt', mode='rb') as f:
f.seek(6, 1)
f.seek(3,1)
#f.seek(300, 1) # 向右可以无限移动
# print(f.read())
res = f.tell()
print(res)
6.1.3 2模式
with open(r'a.txt', mode='rb') as f:
f.seek(-6, 2)
f.seek(-3, 2)
# f.seek(-30, 2) # 超过了最大的移动距离
# f.seek(0, 0)
# f.seek(-1, 0) # 报错,开头无法再向左移动了
res = f.tell() # 返回文件指针的时候都是从左向右数
print(res)
6.1.4 小作业
# 写一个动态更新显示最新日志的一个小程序
import time
with open(r'a.txt', mode='rb') as f:
# 将指针跳到文件末尾
f.seek(0, 2)
while 1:
res = f.readline()
if len(res) == 0:
time.sleep(0.5)
else:
print(res.decode(), end='')
# 追加日志的程序
with open(r'a.txt',mode='a+',encoding='utf-8') as f:
f.write('20201120 张三消费1000元
')
6.2 修改文件的两种方式
首先补充一个知识点,就是硬盘的数据是不能修改的,内存的数据是可以修改的。比如有一个文本文件,里面有
数据:张三 北京大学 大一学生,你想修改成 张三 男 20 北京大学 大一学生。平常我们用的文本编辑器是可以
直接修改的。但是你用python代码试一下看一下结果。
with open(r'a.txt',mode='r+t',encoding='utf-8') as f:
f.seek(10,0) # 指针移动到张三后面
f.write('男 20')
# 当你再次打开文件你会发现 男 20会把北京大学覆盖了
原因是硬盘不支持修改,或者说成是追加。如果一块数据发生移动,那么在它后面的所有数据都会移动,那么每次
修改文件都会产生这种问题。所以硬盘只能写不能中间插入。而我们平常用的文本编辑器只是把内存中的所有修改
后的数据再保存到硬盘上。
6.2.1 第一种方式:
with open( 'c.txt' ,mode='rt',encoding='utf-8' ) as f:
res = f.read ( )
data = res.replace( 'alex' , 'dsb')
print(data)
with open( 'c.txt' ,mode='wt',encoding='utf-8' ) as f1:
f1.write(data)
6.2.2 第二种方式:
import os
with open(r'c.txt', mode='r', encoding='utf-8') as f,
open(r'.c.txt.swap', mode='w', encoding='utf-8') as f1:
# .原文件名.swap 是linux中的临时交换的文件命名
for line in f:
data = line.replace('alex', 'dsb')
f1.write(data)
os.remove('c.txt') # 删除原文件
os.rename('.c.txt.swap', 'c.txt') # 对新文件重命名