一.文件操作流程
# 1. 打开文件,由应用程序向操作系统发起系统调用open(...),操作系统打开该文件,对应一块硬盘空间,并返回一个文件对象赋值给一个变量f
f = open('a.txt',mode='r',encoding='utf-8') #默认打开模式就为r
# 2. 调用文件对象下的读/写方法,会被操作系统转换为读/写硬盘的操作
data = f.read()
# 3. 向操作系统发起关闭文件的请求,回收系统资源
f.close()

二.资源回收和with上下文管理
1、f.close() # 回收操作系统打开的文件资源
2、del f # 回收应用程序级的变量
# 1、在执行完子代码块后,with 会自动执行f.close()
with open('a.txt',mode='w', encoding='utf-8') as f:
pass
# 2、可用用with同时打开多个文件,用逗号分隔开即可
with open('a.txt',mode='r',encoding='utf-8') as read_f,\
open('b.txt',mode='w', encoding='utf-8') as write_f:
data = read_f.read()
write_f.write(data)
三.指定操作文本文件的字符编码(在t模式下, 必须要指定字符编码)
'''
强调: t和b不能单独使用, 必须跟r/w/a连用
t 文本(默认的模式)
1. 读写都以str(unicode) 为单位的
2. 文本文件
3. 必须指定encoding='utf-8'
'''
# 没有指定encoding参数操作系统会使用自己默认的编码
# linux系统默认utf-8
# windows系统默认是gbk
with open('a', mode='rt', encoding='utf-8') as f:
result = f.read() # t模式会将f.read()读出的结果解码成unicode(内存中)
print(result)
# 内存: utf-8格式的二进制---解码--->unicode
# 硬盘(a内容: utf-8格式的二进制)
四.文件操作模式
4.1控制文件读写的操作
r(默认的):只读
w:只写
a:只追加写
4.1.1r模式+案例
# 1. r(默认的操作模式) : 只读模式, 当文件不存在时报错, 当文件存在时文件指针跳到开始位置
with open('aaa/a', mode='rt', encoding='utf-8') as f:
print('第一次读'.center(10, '-'))
result = f.read() # 把所有内容从硬盘读入内存
print(result)
# 案例
temp = True
list1 = []
while temp:
in_username = input('your name>> ').strip()
in_password = input('your pwd>> ').strip()
list1.append(in_username)
with open('aaa/bbb', mode='rt', encoding='utf-8') as f1:
for line in f1:
name, pwd = line.strip().split(':') # stirp()是去掉字符串两边空白字符, 这里是解压赋值
if in_username == name and in_password == pwd:
print('login successful')
temp = False
break
else:
print('账号或者密码错误 ') # for循环正常结束, 没有被break打断才会执行
if len(list1) == 3:
print('最多只能输入三次, 傻子')
temp = False
4.1.2w模式+案例
# w: 只写模式, 当文件不存在时会创建空文件, 当文件存在时会清空文件, 指着位于开始位置
with open('aaa/ccc.txt', mode='wt', encoding='utf-8') as f:
f.write('哈哈哈\n') # \n表示的是换行
# f.read() # 会报错
# 在以w模式打开文件没有关闭的情况下, 连续写入, 新的内容总是跟在旧的之后
with open('aaa/ddd.txt', mode='wt', encoding='utf-8') as f:
f.write('哈哈1\n')
f.write('哈哈2\n')
f.write('哈哈3\n')
# 案例: w模式用来创建全新的文件
# 文件的copy工具
src_file = input('源文件路径>>>: ').strip()
dst_file = input('源文件路径>>>: ').strip()
with open(f'{src_file}', mode='rt', encoding='utf-8') as f,\
open(f'{dst_file}', mode='wt', encoding='utf-8') as f1:
res = f.read()
f1.write(res)
4.1.3a模式+案例
-
w 模式与a 模式的对比:
1. 相同点: 再打开的文件不关闭的情况下, 连续的写入, 新写的内容会在跟在前写的内容之后
2. 不同点: 以 a 模式重新打开文件, 不会清空原文件内容, 会将文件指针移动到文件末尾, 新写的内容永远写在最后
# 3.a 只追加写, 在文件不存在时会创建空文档, 当文件存在时指针会直接调到末尾
with open('aaa/ddd.txt', mode='at', encoding='utf=8') as f:
f.write('哈哈哈哈哈哈\n')
# w 模式与a 模式的对比
# 1. 相同点: 再打开的文件不关闭的情况下, 连续的写入, 新写的内容会在跟在前写的内容之后
# 2. 不同点: 以 a 模式重新打开文件, 不会清空原文件内容, 会将文件指针移动到文件末尾, 新写的内容永远写在最后
# 案例: a 模式用在原有的文件基础之上写入新的内容, 比如记录日志, 注册
# 注册功能
name = input('your name: ').strip()
pwd = input('your pwd: ').strip()
with open('aaa/ddd.txt', mode='at', encoding='utf-8') as f:
f.write(f'账户名:{name}, 密码:{pwd}\n')
4.1.4+模式
# + 不能单独使用, 必须配合r,w,a 可读可写
with open('aaa/a', mode='r+', encoding='utf-8') as f:
print(f.read())
print(f.write('123'))
4.2控制文件读写内容
"""
控制文件读写内容的方式
t:
1.读写都是以字符串(unicode) 为单位
2.只能针对文本文件
3.必须指定字符编码, 即必须指定encoding参数
b: binary模式
1.读写都是以bytes为单位
2.可以针对所有文件
3.一定不能指定字符编码, 即一定不能指定encoding参数
总结:
1.在操作纯文本文件方面t模式帮我们省去了编码与解码的环节, b模式则需要手动编码与解码, 所以此时t模式更为方变
2.针对非文本文件(如图片, 视频, 音频等) 只能使用b模式
"""
4.2.1t模式+案例
# t模式:如果我们指定的文件打开模式为r/w/a,其实默认就是rt/wt/at
with open('a.txt', mode='rt', encoding='utf-8') as f:
res=f.read()
print(type(res)) # 输出结果为:
with open('a.txt',mode='wt',encoding='utf-8') as f:
s='abc'
f.write(s) # 写入的也必须是字符串类型
# 强调:t 模式只能用于操作文本文件,无论读写,都应该以字符串为单位,而存取硬盘本质都是二进制的形式,当指定 t 模式时,内部帮我们做了编码与解码
4.2.2b模式+案例
with open(r'列表循环删除.jpg', mode='rb') as f:
result = f.read() # 不能指定字符编码, 会报错的, 硬盘的二进制读入内存-->b模式, 不做任何转换, 直接读入内存
print(result) # bytes类型-->当成二进制
print(type(result))
with open(r'a.txt', mode='rb') as f:
result = f.read()
print(result) # b'aaa'
print(type(result))
print(result.decode('utf-8')) # 通过解码获得结果
with open(r'a.txt', mode='wb') as f:
f.write('你好hello'.encode('utf-8'))
# 文件拷贝工具
src_file = input('源文件路径>> ').strip()
dst_file = input('目标文件路径>> ').strip()
with open(fr'{src_file}', mode='rb') as f1, \
open(fr'{dst_file}', mode='wb') as f2:
for line in f1:
f2.write(line)
# 循环读取文件
# 用while实现: 自己控控制每次读取的数据的数据量
with open(r'列表循环删除.jpg', mode='rb') as f:
while True:
result = f.read(1024) # 1024个字节
if len(result) == 0:
break
print(len(result))
4.2.3x模式
'''
x模式(控制文件操作的模式)
x , 只写模式[不可读, 不存在则创建, 存在则报错
'''
with open('a.txt', mode='x', encoding='utf-8') as f:
f.write('aaa')
五.操作文件的方法
5.1(重点)
# 一. 读相关的操作
# read()
# 1. 文件打开方式为文本模式时,代表读取n个字符
# 2. 文件打开方式为b模式时,代表读取n个字节
# 1.readline: 一次读一行,readline()读取出来的数据在后面都有一个\n
# 可以用strip()解决这个问题
with open(r'a.txt', mode='rt', encoding='utf-8') as f:
res1 = f.readline() # 读取一行
print(res1)
while True:
line = f.readline()
if len(line) == 0:
break
print(line)
# readlines() 返回一个列表,列表里面每个元素是原文件的每一行,如果文件很大,占内存,容易崩盘
with open(r'a.txt', mode='rt', encoding='utf-8') as f:
res = f.readlines() # ['你好hello\n', 'aaa\n', 'bbb\n', 'ccc']
print(res) # 默认的结果是列表
# f.read()和f.readlines()都是将内容一次性读入内存, 如果内容过大导致内存溢出,
# 二. 写相关的操作
# f.writelines()
with open(r'a.txt', mode='wt', encoding='utf-8') as f:
# f.writelines('111\n222\n')
list1 = ['111\n', '222\n', '333\n']
# for line in list1: # 循环列表写入
# f.write(line)
f.writelines(list1)
with open('b.txt', mode='wb') as f:
l = [
'1111\n'.encode('utf-8'),
'122\n'.encode('utf-8'),
'333\n'.encode('utf-8')
]
# 如果是纯英文字符, 可以直接加前缀b得到bytes类型
l = [b'111', b'222', b'333']
f.writelines(l)
5.2(了解)
f.readable() # 文件是否可读
f.writable() # 文件是否可读
f.closed # 文件是否关闭
f.encoding # 如果文件打开模式为b,则没有该属性
f.flush() # 立刻将文件内容从内存刷到硬盘
f.name
六.控制文件指针移动
# 大前提:文件内指针的移动都是Bytes为单位的,唯一例外的是t模式下的read(n),n以字符为单位
with open(r'a.txt', mode='rt', encoding='utf-8') as f:
result = f.read(2) # 提取2个字符
print(result)
with open('a.txt',mode='rb') as f:
data = f.read(3) # 读取3个Bytes
# 之前文件内指针的移动都是由读/写操作而被动触发的,若想读取文件某一特定位置的数据,则则需要用f.seek方法主动控制文件内指针的移动,详细用法如下:
# f.seek(指针移动的字节数,模式控制):
# 模式控制:
# 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
# 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
# 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的
# 强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用
6.1(0模式案例)
# 0模式的使用
with open('a.txt',mode='rt',encoding='utf-8') as f:
f.seek(3,0) # 参照文件开头移动了3个字节
print(f.tell()) # 查看当前文件指针距离文件开头的位置,输出结果为3
print(f.read()) # 从第3个字节的位置读到文件末尾,输出结果为:你好
# 注意:由于在t模式下,会将读取的内容自动解码,所以必须保证读取的内容是一个完整中文数据,否则解码失败
with open('a.txt',mode='rb') as f:
f.seek(6,0)
print(f.read().decode('utf-8')) #输出结果为: 好
with open('a.txt', mode='rb') as f:
f.seek(2, 0)
res = f.read()
print(res.decode('utf-8'))
print(f.tell())
6.2(1模式案例)
# 1模式的使用
# b模式不能指定字符编码
with open('a.txt',mode='rb') as f:
f.seek(3,1) # 从当前位置往后移动3个字节,而此时的当前位置就是文件开头
print(f.tell()) # 输出结果为:3
f.seek(4,1) # 从当前位置往后移动4个字节,而此时的当前位置为3
print(f.tell()) # 输出结果为: 7
6.3(2模式案例)
# a.txt用utf-8编码,内容如下(abc各占1个字节,中文“你好”各占3个字节)
abc你好
# 2模式的使用
with open('a.txt',mode='rb') as f:
f.seek(0,2) # 参照文件末尾移动0个字节, 即直接跳到文件末尾
print(f.tell()) # 输出结果为:9
f.seek(-3,2) # 参照文件末尾往前移动了3个字节
print(f.read().decode('utf-8')) # 输出结果为:好
# 小练习:实现动态查看最新一条日志的效果
import time
with open('access.log', mode='rb') as f:
f.seek(0,2)
while True:
line = f.readline()
if len(line) == 0:
# 没有内容
time.sleep(0.5)
else:
print(line.decode('utf-8'),end='')
七.文件修改的两种方式
7.1方式一
# 方式一: 文本编辑采用的就是这种方式
# 实现思路: 将文件内容一次性全部读入内存, 然后在内存中修改完毕后再覆盖写回原文件
# 优点: 在文件修改的过程中同一份数据只有一份
# 缺点: 会过多占用内存
with open(r'a.txt', mode='r', encoding='utf-8') as f:
res = f.read() # 读取所有内容
data = res.replace('西施', '小乔') # 将内容修改, 赋值给变量存在内存中
with open(r'a.txt', mode='w', encoding='utf-8') as f1:
f1.write(data) # 覆盖写入硬盘
7.2方式二
# 方式二
# 要用到os模块
# 实现思路: 以读的方式打开原文件, 以写的方式打开一个临时文件, 一行行读取原文件内容,
# 修改完后写入临时文件, 删除原文件, 将临时文件名字改为原文件名字
# 优点: 不会占用过多的内存
# 缺点: 在文件修改过程中同一份文件存了两份, 占用硬盘内存
import os
with open(r'a.txt', mode='r', encoding='utf-8') as f, \
open(r'.a.txt.swap', mode='w', encoding='utf-8') as f1: # .'文件'.swap表示隐藏文件
for line in f:
f1.write(line.replace('小乔', '西施')) # 循环写入
os.remove('a.txt')
os.rename('.a.txt.swap', 'a.txt')
八.seek的实例
8.1实时监控日志的增加的内容
"""
@作者: roc小白
@ QQ: 845726666
@专栏: https://www.cnblogs.com/jupeng/p/14939889.html
第一: 往文件里面写内容
"""
with open(r'a.txt', mode='a', encoding='utf-8') as f:
f.write('\n大乔, 17 female')
# 二. 实时打印添加的内容
import time
with open(r'a.txt', mode='rb') as f:
f.seek(0, 2) # 指针移到末尾
# 死循环, 一直读取最后一行数据
while True:
line = f.readline()
if not line:
time.sleep(0.3)
else:
print(line.decode('utf-8'), end='')