Python——文件处理
文件
什么是文件
- 文件是操作系统提供给用户/应用程序操作硬盘的一种虚拟概念(接口)
为什么要用文件
- 用户/应用程序可以通过文件将数据永久保存到硬盘中,即操作文件就是操作硬盘。
- 用户/应用程序直接操作的是文件,对文件进行的所有操作都是在向操作系统发送系统调用,然后再由操作系统将其转换成具体的硬盘操作
如何用文件
-
控制文件读写内容的模式:t和b, 强调:t和b不能单独使用,必须跟r/w/a连用
# t文本(默认的模式) ''' 1.读写都以str(unicode)为单位的 2、文本文件 3、必须指定encoding="存入硬盘时的编码格式" ''' # b二进制/bytes
-
控制文件读写操作的模式:r/w/a/+
-
open()的使用
一、文件操作的基本流程
1.1 基本流程
-
打开文件
# windows路径分隔符问题 # open('C:\a\nb\c\d.txt') # 解决方案一:推荐 open(r'C:\a\nb\c\d.txt') # 解决方案二: open('C:/a/nb/c/d.txt') # 相对路径,从当前路径下开始找,如果文件不存在,则报错 f=open('a.txt') # f的值是一种变量,占用的是应用程序的内存空间,向操作系统发送系统调用并在操作系统中打开资源空间 print(f) # 文件类型
-
读/写文件
# 应用程序对文件的读写请求都是在向操作系统发送系统调用 # 然后由操作系统控制硬盘把输入读入内存,或者写入硬盘 res = f.read() # 读文件 print(res) f.write("写入内容") # 写文件
-
关闭文件
f.close() # 回收操作系统资源 f.read() # 变量f存在,但是不能再读了 # del f # 回收应用程序资源
1.2 资源回收与with上下文管理
# with会在子代码块全部执行完毕后自动执行.close()来关闭文件
# 文件对象又称文件句柄
with open('a.txt', mode='rt') as f1:
res = f1.read()
print(res)
with open('a.txt', mode='rt') as f1,\
open('b.txt',mode='rt') as f2:
res1 = f1.read()
res2 = f2.read()
print(res1)
print(res1)
1.3 指定操作文本文件的字符编码
# 没有指定encoding参数,操作系统会使用自己默认的编码
# linux系统默认:utf-8
# mac系统默认:utf-8
# windows系统默认gbk
with open('c.txt', mode='rt', encoding='utf-8') as f:
# 读文件
res = f.read() # 因为t模式的影响,会将f.read()读出的结果解码成unicode
print(res,type(res))
# 写文件,t模式有局限性,就是只限于文本文件
f.write('哈哈哈')
# 内存:utf-8格式的二进制———————解码———————》unicode
# 硬盘(c.txt内容:utf-8格式的二进制)
二、文件的操作模式
2.1 控制文件读写操作的模式
-
2.1.1 案例一:r 模式的使用
# 登陆验证功能 inp_user = input(">>>>your name:").strip() inp_pwd = input(">>>>your password:").strip() with open('user.txt', mode='rt', encoding='utf-8') as f: for line in f: username, password = line.strip().split(':') if inp_user == username and inp_pwd == password: print('login success') break else: print('账号或密码错误')
-
2.1.2 案例二:w 模式的使用
# w模式用来创建全新的文件 # 文本文件的拷贝工具 src_file = input('源文件路径>>:').strip() copy_file = input('源文件路径>>:').strip() with open(r'{}'.format(src_file), mode='rt', encoding='utf-8') as f1,\ open(r'{}'.format(copy_file), mode='wt', encoding='utf-8') as f2: # res = f1.read() # f2.write(res) for line in f1: f2.write(line)
-
2.1.3 案例三:a 模式的使用
# a模式用来再原有的文件内容的基础之上写入新的内容,比如记录日志 # 注册功能 name = input('>>>>your name:').strip() pwd = input('>>>>your password:').strip() with open('db.txt', mode='at', encoding='utf-8') as f: f.write('{name}:{pwd}\n'.format(name,pwd))
-
2.1.4 案例四:+ 模式的使用(了解)
# +不能单独使用,必须配合r、w、a with open('g.txt',mode='r+t',encoding='utf-8') as f: # print(f.read()) f.write('中国') with open('g.txt',mode='w+t',encoding='utf-8') as f: f.write('111\n') f.write('222\n') f.write('333\n') print('====>',f.read()) with open('g.txt',mode='a+t',encoding='utf-8') as f: print(f.read()) f.write('444\n') f.write('5555\n') print(f.read())
2.2 控制文件读写内容的模式
-
2.2.1 案例一:t 模式的使用
# t模式的存操作 ''' 1、读写都以字符串(unicode)为单位 2、只能针对文本文件 3、必须指定encoding字符编码 ''' # 1、r(默认的操作模式):只读模式,当文件不存在时报错,当文件存在时文件指针跳到开始位置 with open('r.txt', mode='rt', encoding='utf-8') as f: print('第一次读'.center(50,'*')) res = f.read() # 把所有内容从硬盘读入内存 print(res) print('第二次读'.center(50,'*')) res1 = f.read() # 把所有内容从硬盘读入内存 print(res1) # 2、w:只写模式,当文件不存在时会创建空文件,当文件存在时会清空文件,指针位于开始位置 ''' 强调1: 强调2:如果重新以w模式打开文件,则会清空文件内容 ''' with open('w.txt', mode='wt', encoding='utf-8') as f: f.write('哈哈哈\n') f.write('哈哈哈\n') f.write('哈哈哈\n') # 3、a:只追加写,在文件不存在时会创建空文档,在文件存在时文件指针会直接跳到末尾 with open('w.txt', mode='at', encoding='utf-8') as f: f.write('哈哈哈\n') f.write('哈哈哈\n') f.write('哈哈哈\n') ''' 强调w模式与a模式的异同: 相同点:再打开的文件不关闭的情况下,连续的写入,新内容总会跟在前写的内容之后 不同点:以a模式重新打开文件,不会清空原文件内容,会将文件指针直接移动到文件末尾 '''
-
2.2.2 案例二: b 模式的使用四 操作文件的方法
# 错误演示 with open(r'a.mp4', 'rt') as f: f.read() # b: binary模式 ''' 1、读写都是以bytes为单位 2、可以针对所有文件 3、一定不能指定字符编码 ''' with open(r'a.mp4', 'rb') as f: f.read() # bytes=>二进制 ''' 得到bytes类型的三种方式 1、字符串编码之后的结果 '上'.encode('utf-8') bytes('上', encoding='utf-8') 2、b'必须是纯英文字符' 3、b模式下打开文件,f.read()读出的内容 ''' # 文件拷贝工具 src_file = input('请输入源文件路径:').strip() copy_file = input('请输入文件保存的路径:').strip() file_name = src_file.split('\\')[-1] with open(r'{}'.format(src_file), 'rb') as rf,\ open(r'{src}\{copy}'.format(src=copy_file, copy=file_name), 'wb') as wf: for line in rf: wf.write(line) # 循环读取文件 # 方式一:自己控制每次读取的数据量 with open(r'test.jpg', 'rb') as f: while True: res = f.read(1024) # 一次只读1024个字节 if len(res) == 0: break print(len(res)) # 方式二:以行为单位,一次读取一行的数据 with open(r'test.jpg','rb') as f: for line in f: print(line)
-
2.2.3 x模式(控制文件操作的模式)了解
# x, 只写模式【不可读;不存在则创建,存在则报错】
-
总结:
- 在操作纯文本文件方面t模式帮我们省去了编码与解码的环节,b模式则需要手动编码与解码,所以此时t模式更为方便
- 针对非文本文件(如图片、视频、音频等)只能使用b模式
三、操作文件的方法
3.1 重点
# 一:读相关操作
# 1、readline:一次读一行
with open(r'test.txt', 'rt', encoding='utf-8') as f:
while True:
line = f.readline()
if len(line) == 0:
break
print(line)
# 2、readlines:
with open(r'test.txt', 'rt', encoding='utf-8') as f:
while True:
line = f.readlines()
print(line)
'''
强调:
f.read()与f.readlines()都是将内容一次性读入内容,如果内容过大会导致
'''
# 二:写相关操作
# 3、f.writelines():
with open(r'test.txt', 'wt', encoding='utf-8') as f:
# f.write('1111\n222\n3333\n')
# l=['1111\n', '2222', 3333] # 会报错,应为是t模式,必须是str类型
l=['1111\n', '2222', '3333']
# for line in l:
# f.write(line)
f.writelines(l)
with open(r'test.txt', 'wb') as f:
# 补充1:纯英文字符,可以不做转换通过加前缀b得到bytes类型
l=[b'1111\n', b'2222', b'3333']
# 补充2:'上'.encode('utf-8')等同于bytes('上',encoding='utf-8')
l=['矮跟'.encode('utf-8'), '大'.encode('utf-8'), 'SB'.encode('utf-8')]
l=[
bytes('矮跟',encoding='utf-8'),
bytes('大',encoding='utf-8'),
bytes('SB',encoding='utf-8')
]
f.writelines(l)
# 4、flush (大多数情况不用)
with open(r'test.txt', 'wt', encoding='utf-8') as f:
f.write('哈哈哈')
f.flush() # 告诉操作系统,强制立刻将数据写入内存
3.2 了解
with open(r'test.txt', 'wt', encoding='utf-8') as f:
print(f.readable()) # 文件是否可读
print(f.writable()) # 文件是否可写
print(f.closed) # 文件是否关闭
print(f.encoding) # 如果文件打开模式为b,则没有该属性
print(f.flush()) # 立刻将文件内容从内存刷到硬盘
print(f.name) # 获取文件名
四、主动控制文件内指针移动
'''
指针移动的单位都是以bytes/字节为单位
只有一种情况下特殊:
t模式下的read(n),n代表的是字符个数
'''
with open('test.txt', 'rt', encoding='utf-8') as f:
res=f.read(4)
print(res)
# f.seek(n,模式):n指的是移动的字节个数
# 模式0:参照物是文件开头位置
f.seek(9,0) # 9
f.seek(3,0) # 3
# 模式1:参照物是当前指针所在位置
f.seek(9,1) # 9
f.seek(3,1) # 12
# 模式2:参照物是文件末尾位置,应该倒着移动
f.seek(-9,2) # 3
f.seek(-3,2) # 9
'''
强调:只有0模式可以在t下使用,1、2必须在b模式下使用
'''
# f.tell() # 获取文件指针当前位置
4.1 案例一:0模式详解
with open('test.txt',mode='rb') as f:
f.seek(9,0) # 9
f.seek(3,0) # 3
# print(f.tell())
f.seek(4,0) # 4
res=f.read()
print(res.decode('utf-8'))
4.2 案例二:1模式详解
with open('test.txt',mode='rb') as f:
f.seek(9,1) # 9
f.seek(3,1) # 12
print(f.tell())
4.3 案例三:2模式详解
with open('test.txt',mode='rb') as f:
f.seek(-9,2)
# print(f.tell())
f.seek(-3,2)
# print(f.tell())
print(f.read().decode('utf-8'))
4.4 f.seek的应用(tail -f accsee.log程序讲解)
# 追写文件内容的程序test.py
with open(r'access.log', 'at', encoding='utf-8') as f:
f.write('202003111112 egon转账200w\n')
# 监测追写内容的程序tail.py
import time
with open(r'access.log', 'rb') as f:
f.seek(0, 2)
while True:
line = f.readline()
if len(line) == 0:
time.sleep(0.3)
else:
print(line.decode('utf-8'), end='')
五、文件的修改
# 文件a.txt内容如下
张一蛋 山东 179 49 12344234523
李二蛋 河北 163 57 13913453521
王全蛋 山西 153 62 18651433422
# 执行操作
with open('a.txt',mode='r+t',encoding='utf-8') as f:
f.seek(9)
f.write('<妇女主任>')
# 文件修改后的内容如下
张一蛋<妇女主任> 179 49 12344234523
李二蛋 河北 163 57 13913453521
王全蛋 山西 153 62 18651433422
# 强调:
# 1、硬盘空间是无法修改的,硬盘中数据的更新都是用新内容覆盖旧内容
# 2、内存中的数据是可以修改的
5.1 文件修改方式一
# 文本编辑器采用的就是这种方式
# 实现思路:将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件
# 优点: 在文件修改过程中同一份数据只有一份
# 缺点: 会过多地占用内存
with open('db.txt',mode='rt',encoding='utf-8') as f:
data=f.read()
with open('db.txt',mode='wt',encoding='utf-8') as f:
f.write(data.replace('kevin','SB'))
5.2 文件修改方式二
# 实现思路:以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,
# 修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名
# 优点: 不会占用过多的内存
# 缺点: 在文件修改过程中同一份数据存了两份
import os
with open('db.txt',mode='rt',encoding='utf-8') as read_f,\
open('.db.txt.swap',mode='wt',encoding='utf-8') as wrife_f:
for line in read_f:
wrife_f.write(line.replace('SB','kevin'))
os.remove('db.txt')
os.rename('.db.txt.swap','db.txt')