文件处理
一 引入
应用程序运行过程中产生的数据最先都是存放于内存中的,若想永久保存下来,必须要保存于硬盘中。应用程序若想操作硬件必须通过操作系统,而文件就是操作系统提供给应用程序来操作硬盘的虚拟概念,用户或应用程序对文件的操作,就是向操作系统发起调用,然后由操作系统完成对硬盘的具体操作。
二 文件的介绍
2.1 文件的概念
文件:就是操作系统显示给用户可以直接操作硬盘的快捷方式
文件的类型:txt word excel py
有了文件的概念,我们无需再去考虑操作硬盘的细节,只需要关注操作文件的流程:
# 代码打开的两种方式:
# 方式1:
f=open('a.txt', 'r', encoding='utf-8')
# 是让操作系统打开文件,代码只是发出指令
# print(f) # <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf8'> 文件句柄
# 2.操作文件
print(f.read()) # 读出a.txt文件中的内容
# 3.关闭文件
f.close()
# 方式2:
# 2. with上下文管理器
with open(r'D:\python25\day09\a.txt', 'r', encoding='utf-8') as f2:
print(f2.read()) # 自动关闭文件
open关键字:
"""
语法格式:
open('文件路径', '读写模式', '字符编码')
'文件路径', '读写模式'是必须要写的
encoding是可选的
"""
2.1 文件路径
# open寻找文件的方式:先在当前文件中寻找文件名,所以,如果是当前文件中,‘文件路径’可以直接写相对路径,如果是其他文件中,必须写绝对路径
open(r'a.txt', 'r', encoding='utf-8') # 相对路径
open(r'F:\pr\py\a.txt', 'r', encoding='utf-8') # 绝对路径
'''
r的作用:
当路径中可能存在有转义的字符时,字符串前面加上r来解决转义的问题
'''
三、文件的操作模式
3.1 文件的读写模式
# r(默认的):只读
# w:只写
# a:只追加写
3.1.1 r模式
r>>> read: 只读 # 只能读,不能写
路径不存在时,报错
路径存在,正常打开文件
with open(r'a.txt', 'r', encoding='utf-8') as f:
print(f.read())
3.1.2 w模式
w>>> write:只写 # 只能写,不能读
文件路径不存在时,自动创建。文件路径存在时 先清空文件内容之后在写入
with open('b.txt', 'w', encoding='utf-8') as f:
pass
# 2.1 会先清空文件中的内容(只要点击Run就会清空文件)2. 再进行写内容
'''写文件的是一定要小心,它会清空文件的'''
with open('a.txt', 'w', encoding='utf-8') as f:
f.write('oldboy\n') #运行多次,文件中也只有一行
# 2.2文件中想要写多行
with open('a.txt', 'w', encoding='utf-8') as f:
f.write('oldboy\n')
f.write('oldboy\n')
f.write('oldboy\n')
f.write('oldboy\n')
''' 在多行一起write时,如果不使用分隔符分割,都会输出在一行里面,不会帮你自动分割,想要换行我们必须自己加入\n分隔符'''
3.1.3 a 模式
a >>> append: 追加
路径不存在时,自动创建文件,路径存在时,不会清空文件内容,而是在文件末尾等待新内容的添加
with open('a.txt', 'a', encoding='utf-8') as f:
f.write('oldgirl\n')
3.2 控制文件读写内容的模式
r w a读取
大前提: tb模式均不能单独使用,必须与r/w/a之一结合使用
t(默认的):文本模式
1. 只能操作文本文件
2. 都是以字符为单位
3. rt wt at => t可以省略
4. encoding参数必须写
b模式:二进制
1. 能操作任何的数据类型,eg:文本,视频,音频...
2. 写法:rb ab wb >>>: 此时的b不能省略
3. encoding参数不写
4. 就是以字节为单位
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 模式时,内部帮我们做了编码与解码
3.2.2 案例二: b 模式的使用
# 用二进制的模式将'你好'写入'b.txt'文件
with open('b.txt', 'wb') as f:
s = '你好'
f.write(s.encode('utf8'))
# 读取'b.txt'文件
with open('b.txt', 'rb') as f:
print(f.read().decode('utf8'))
四、文件的操作方法
# 读
f.read() # 读取所有内容,执行完该操作后,文件指针会移动到文件末尾
f.readline() # 读取一行内容,光标移动到第二行首部
f.readlines() # 读取每一行内容,存放于列表中
f.readable() # 是否可读,返回布尔值
# 写
f.write('hello\n') # 写入内容到文件
f.writable() # 是否可写
f.writelines(['333\n','444\n']) # 写入多行到文件中,要以列表形式
4.1 读方法
4.1.1 读取的不同用法
# 用法1:
with open(r'a.txt', 'r', encoding='utf-8') as f:
print(f.read())
# 输出:
# oldboy
# oldboy
# oldboy
# 用法2:
with open(r'a.txt', 'r', encoding='utf-8') as f:
data = f.read()
print(data)
print(data) # 用with,后面的代码也可以使用with里定义的数据
# 输出:
# oldboy
# oldboy
# oldboy
# oldboy
# oldboy
# oldboy
4.1.2 read()
with open(r'a.txt', 'r', encoding='utf-8') as f:
print(f.read()) # 一次性读取文件内所有的数据
print(f.read()) # 后面两行读取的是空格
print(f.read())
'''一次性读取文件所有的数据就会存在漏洞:导致内存溢出'''
4.1.3 readline()
with open(r'a.txt', 'r', encoding='utf-8') as f:
print(f.readline()) # 一次读取一行,输出内容会有空格,因为txt文件中的数据换行后默认存在一个换行符,而一个换行符也属于一个字符串格式,实际上第一行数据是('oldboy\n')
print(f.readline()) # 一次读取一行
print(f.readline()) # 一次读取一行
4.1.4 readlines()
'''把文件内的一行一行数据组装成列表元素返回,注意末尾的换行符'''
with open(r'a.txt', 'r', encoding='utf-8') as f:
print(f.readlines())
# 输出:['你好洋哥\n', '你好洋哥\n', '你好洋哥\n']
4.1.5 readable()
# 是否可读,布尔值
with open(r'a.txt', 'r', encoding='utf-8') as f:
print(f.readable()) # True
4.2 写方法
4.2.1 write('hello\n')
with open('a.txt', 'w', encoding='utf-8') as f:
f.write('hello\n')
f.write('hello\n')
f.write('hello\n')
4.2.2 writable()
with open('a.txt', 'w', encoding='utf-8') as f:
print(f.writable()) # True
print(f.readable()) # False
4.2.3 writelines()
with open('a.txt', 'w', encoding='utf-8') as f:
print(f.writelines(['你好洋哥1\n', '你好洋哥2\n', '你好洋哥3\n']))
五、文件读操作优化
5.1 for循环一行一行读取
with open('a.txt', 'r', encoding='utf-8') as f:
print(f.read())
'''文件句柄f支持for循环'''
for line in f:
print(line) # 一次代码一行数据
'''以后读取文件数据的时候,都使用for循环去一行一行的读取,不会出现内存溢出的情况'''
5.2 flush()
# flush():把内存中得数据立刻刷到硬盘中
with open('a.txt', 'w', encoding='utf-8') as f:
f.write('hello')
f.flush()
六、文件二进制读操作(了解)
1. r模式
read()里面的数字代表的是一个字符
2. b模式
read()里面的数字代表的是一个字节
练习如下:
# # 用r模式读取文件
# with open('a.txt', 'r', encoding='utf-8') as f:
# print(f.read())
# # 用二进制rb模式读取文件
# with open('a.txt', 'rb') as f:
# print(f.read().decode('utf-8'))
with open(r'a.txt', 'r', encoding='utf-8') as f:
print(f.read(4))
# 输出结果:你hel
with open(r'a.txt', 'rb') as f:
# print(f.read(4)) # b'\xe4\xbd\xa0h'
# 不加解码,输出是二进制。加上解码,才能看出是什么字节
print(f.read(4).decode('utf-8'))
# 输出结果:你h
'''1个中文汉字占用3个bytes, 如果文件中第一个是汉字,你read()中的数字<3,就会报错,因为不能将汉字拆分'''
七、文件的指针移动(了解)
f.seek可以控制指针移动,总共有3种模式:
1. offset参数
偏移量,移动的位置
如果是正数,从左往右读取
如果是负数,从右往左读取
2. whence参数
# 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
# 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
# 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的
7.1 0模式
# 0模式
with open('a.txt', 'rb') as f:
print(f.read().decode('utf-8'))
f.seek(3, 0)
print(f.read().decode('utf-8'))
# 输出结果是:
# helloword
# mynameiskevin
# loword
# mynameiskevin
7.2 1模式
# 1模式
with open('a.txt', 'rb') as f:
print(f.read(3).decode('utf-8'))
f.seek(3, 1)
print(f.read().decode('utf-8'))
# 输出结果是:
# hel
# ord
# mynameiskevin
7.3 2模式
# 2模式
with open('a.txt', 'rb') as f:
print(f.read(3).decode('utf-8'))
f.seek(-3, 2)
print(f.read().decode('utf-8'))
# 输出结果是:
# hel
# vin
# 小练习:实现动态查看最新一条文件的效果
import time
with open(r'a.txt', 'rb') as f:
f.seek(0, 2) # 指针移动到末尾,移动0个bytes
while True:
line = f.readline() # 读取一行
if len(line) == 0:
# 如果没有内容,停0.5秒
time.sleep(0.5) # 停留0.5秒
else: # 如果有内容,打印数据
print(line.decode('utf-8'), end='')
# 运行后光标一直在闪,在a.txt文件中输入内容,保存(ctrl+s),后面输的代码就会被输出
八、文件的修改(了解)
# 文件a.txt内容如下
张一蛋 山东 179 49 12344234523
李二蛋 河北 163 57 13913453521
王全蛋 山西 153 62 18651433422
# 执行操作
with open('a.txt',mode='r+t',encoding='utf-8') as f: # 'r+'模式:可读可写
f.seek(9) # 默认0模式
f.write('<妇女主任>')
# 文件修改后的内容如下
张一蛋<妇女主任> 179 49 12344234523
李二蛋 河北 163 57 13913453521
王全蛋 山西 153 62 18651433422
# 强调:
# 1、硬盘空间是无法修改的,硬盘中数据的更新都是用新内容覆盖旧内容
# 2、内存中的数据是可以修改的
文件对应的是硬盘空间,硬盘不能修改对应着文件本质也不能修改,
那我们看到文件的内容可以修改,是如何实现的呢?
大致的思路是将硬盘中文件内容读入内存,然后在内存中修改完毕后再覆盖回硬盘
具体的实现方式分为两种:
8.1 文件的修改方式一
# 实现思路:将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件
# 优点: 在文件修改过程中同一份数据只有一份
# 缺点: 会过多地占用内存
# 1. 读取b.txt文件数据
with open('b.txt', 'r', encoding='utf-8') as f:
data = f.read()
print(data) # kevin kevin kevin kevin say hello world
new_data = data.replace('kevin', 'jack')
# 2. 把新的数据再次写入文件
with open('b.txt', 'w', encoding='utf-8') as f1:
f1.write(new_data)
8.2 文件的修改方式二
# 实现思路:以读的方式打开原文件,以写的方式打开一个文件,一行行读取原文件内容,修改完后写入新文件中...,删掉原文件,将新文件重命名原文件名
# 优点: 不会占用过多的内存
# 缺点: 在文件修改过程中同一份数据存了两份
import os
with open('a.txt', 'rt', encoding='utf-8') as read_f:
with open('b.txt', mode='wt', encoding='utf-8') as write_f:
for line in read_f:
write_f.write(line.replace('jack', 'kevin'))
os.remove('a.txt') # 删除源文件
os.rename('b.txt', 'a.txt') # 文件重命名
# 可以用一个with同时打开两个文件,代码如下:
# with open('a.txt', 'rt', encoding='utf-8') as read_f, open('b.txt', mode='wt', encoding='utf-8') as write_f:
# for line in read_f:
# write_f.write(line.replace('jack', 'kevin'))