Python文件操作
2020-08-06 本文已影响0人
xilifeng
一. 操作文件的三个步骤: 打开文件, 读写, 回收OS资源
1. 方式一: 打开文件, 读写, 回收OS资源
f = open(r"Y:\new\a.txt", mode="rt", encoding="utf-8") # t读写文件的格式为字符串,open返回值文件对象/文件句柄,是程序的变量值
data = f.read() # 硬盘上的文件内容读到内存中,OS根据指定的encoding把二进制数转成t(unicode字符串)给程序,t仅限文本文件
print(data, type(data))
f.close() # close回收OS资源,close后不能再read,文件对象是程序的变量值,是程序的资源,仍然存在
2. 方式二: 上下文管理 with 会自动调用f.close(), f1.close(), f2...
with open(r"Y:\new\a.txt", mode="rt", encoding="utf-8") as f, \
open(r"Y:\new\b.txt", mode="rt", encoding="utf-8") as f1:
pass # 也可以用...
二. 文件的打开模式
1. 控制文件读写操作的模式: r只读, w只写,擦写, a只追加写, +加上原没有的操作, r+, w+, a+: 读写
1.1 r: 只读, 不可写, 默认模式, 指针在文件开头
with open(r"Y:\new\a.txt", mode="rt", encoding="utf-8") as f:
print(f.readable()) # True
print(f.writable()) # False
1.2 w: 只写, 不可读, 创建一个新文档, 同名文件不存在则为创建, 同名文件存在则覆盖, 指针跳到文件开头
with open(r"Y:\new\b.txt", mode="wt", encoding="utf-8") as f:
f.write("你好\nHello World.")
f.write("打开文件不关的情况下,指针跟着移动,后续新写入的内容则是追加.但是文件关了重新打开,再写=擦写")
1.3 a: 只追加写,不可读,文件不存在则新建一个空文件;文件存在则不清空,无论是不是重新打开或关了,文件指针都跳到文件末尾,追加写入新内容
1.4 r+, w+, a+: 读写
2. 控制文件读写内容的模式, t,b须和r,w,a连用
2.1 t: 默认, 文本格式, 读写都是str字符串, 数字不可以, 仅限文本文件, 必须指定encoding参数
2.2 b: 原生格式, 读写都是bytes为单位, 能用于所有文件, 一定不能指定encoding参数
with open(r"Y:\new\a.txt", mode="wb") as f:
f.write("原生格式写入新内容".encode("utf-8"))
3. 案例: 文件copy程序
src_file = input("源文件路径: ").strip()
dst_file = input("目标文件路径: ").strip()
with open(r"%s" % src_file, mode="rb") as f1, \
open(r"%s" % dst_file, mode="wb") as f2:
3.1 方法一: 一次性读入内存
data = f1.read()
f2.write(data)
3.2 方法二: 逐行读取写入
for line in f1:
f2.write(line)
三. 文件内指针移动: seek是无IO操作的移动, read是有IO操作的被动移动
file.seek(移动的字节个数, 三种模式) # 3种模式都以字节为移动单位
1. 三种模式:
1.1 模式 0 : 从文件开头为起点,f.seek(3, 0)从头往右移动3个字节
1.2 模式 1 : 从当前指针所在的位置,f.seek(6, 1)前面停在第3字节,现停在第9字节
1.3 模式 2 : 从文件末尾为起点,f.seek(-6, 2)从末尾往左移6字节,正数右移,负数左移
2. 三种模式适用范围:
2.1 mode="t"模式: seek只能用 模式 0, 而另一种移动read(n)中n,只有mode="t"模式下,代表字符个数
with open(r"Y:\new\a.txt", mode="rt", encoding="utf-8") as f: # 文件内容:hello你好
print(f.read(6)) # 打印: hello你 ,以字符为单位,中英文都是一个字符
2.2 mode="b"模式: seek都可用 模式 0, 1, 2, seek和read(n)中n都代表字节个数
with open(r"Y:\new\a.txt", mode="rb") as f: # 文件内容:hello你好
print(f.read(6)) # 打印: b'hello\xe4' ,以字节为单位,utf-8中,中文3字节,英文1字节
f.seek(0, 2) # 指针跳到末尾
print(f.tell()) # tell获取从文件开头到当前位置的总字节数
3. 示例: 模拟写tail -f access.log
3.1 测试程序写入日志
import time
with open('access.log', mode='at', encoding='utf-8') as f:
f.write("%s %s\n" % (time.strftime("%Y-%m-%d %H:%M:%S %p"), "程序写的日志内容"))
3.2 模拟写tail -f 查看日志
import time
with open('access.log', mode='rb') as f:
f.seek(0, 2)
while True:
line = f.readline() # readline每次读一行
if len(line) == 0: # 没有日志的时候, 稍微等待, 减少负载开销
time.sleep(0.5) # 死循环会不停调用,负载会升高
else:
print(line.decode('utf-8'), end='') # 原日志中\n,打印默认也有,会有两个\n去掉一个
四. 操作文件的方法
1. f.read(n) 从当前位置读到末尾,只有mode="t"模式下,read(n)中n代表字符个数
2. f.readlines() rt模式,以行为单位读取文本,存入列表['hello你好\n','l2\n','l3']
3. f.writelines("hello") wt模式, 循环分别写入5个字母, 而f.write("hello")一次写入
with open(r"Y:\new\a.txt", mode="wt", encoding="utf-8") as f:
lines = ["aa\n", "bb\n", "cc\n"]
for line in lines: # 这两行效果等于f.writelines(lines), 实质就是for循环取值
f.write(line)
4. f.flush() 内存数据刷入硬盘
5. f.truncate(size) 截断,从开头保留n个字节,后面全删,是写操作,故要r+,a模式
with open(r"Y:\new\a.txt", mode="r+t", encoding="utf-8") as f:
f.truncate(3)
五. 修改文件的方式
1. 方式一: 硬盘文件一次性读入内存, 内存中修改完, 再覆盖原文件,不额外占用硬盘空间, 过多占用内存
with open(r"Y:\new\a.txt", mode="rt", encoding="utf-8") as f1:
data = f1.read()
res = data.replace("source", "target")
with open(r"Y:\new\a.txt", mode="wt", encoding="utf-8") as f2:
f2.write(res)
2. 方式二: 逐行读源文件,内存中修改后,逐行写入临时文件,删除源,临时文件重命名,额外占用硬盘空间, 节省内存
import os
with open(r"Y:\new\a.txt", mode="rt", encoding="utf-8") as f1, \
open(r"Y:\new\.a.txt.swp", mode="wt", encoding="utf-8") as f2:
for line in f1:
f2.write(line.replace("source", "target"))
os.remove(r"Y:\new\a.txt")
os.rename(r"Y:\new\.a.txt.swp", r"Y:\new\a.txt")