文本文件读写:read()、readline()、readlines()方法、write()与writelines()方法、flush()与缓冲区
文件读写,说白了就是让程序和硬盘上的文本文件「对话」。我刚开始学的时候,总觉得这玩意儿不就是打开、读、写、关闭嘛,有啥好讲的?直到我在项目中踩过几个坑,才发现里面的门道还真不少。
今天咱们就把文本文件的读写方法彻底捋一遍。你想想看,如果连文件都操作不明白,后面的数据持久化、日志记录、配置管理全都得抓瞎。
一、读取文件的三种姿势
Python 提供了三种读取文件的方法,各有各的脾气。我个人的习惯是:看文件大小选方法。
1. read() —— 一口吞
read() 方法会把文件内容一次性全部读出来,返回一个字符串。简单粗暴,但容易撑到。
with open('example.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)
当然,你也可以给 read() 传一个数字参数,指定读取的字符数:
with open('example.txt', 'r', encoding='utf-8') as f:
chunk = f.read(100) # 只读前100个字符
print(chunk)
2. readline() —— 一口一口吃
readline() 每次只读一行,包括行尾的换行符。适合逐行处理,内存友好。
with open('example.txt', 'r', encoding='utf-8') as f:
line = f.readline()
while line:
print(line, end='') # 去掉多余的换行
line = f.readline()
for line in f 就是基于 readline() 实现的,更简洁。我一般直接用 for 循环,除非需要手动控制读取进度。
3. readlines() —— 分盘装
readlines() 会把所有行读到一个列表里,每行是一个元素。适合行数不多、需要随机访问某一行的情况。
with open('example.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
for i, line in enumerate(lines):
print(f"第{i+1}行: {line}", end='')
二、写入文件的两种方式
写文件比读文件稍微复杂一点,因为涉及到「覆盖」还是「追加」的问题。
1. write() —— 写字符串
write() 方法把字符串写入文件,不会自动加换行符。你需要手动加 \n。
with open('output.txt', 'w', encoding='utf-8') as f:
f.write('第一行\n')
f.write('第二行\n')
f.write('第三行')
'w' 会覆盖原有内容,'a' 是追加。我经常在写日志时用 'a' 模式,这样不会丢失历史记录。
2. writelines() —— 写列表
writelines() 接收一个可迭代对象(比如列表),把每个元素写入文件。注意:它不会自动加换行符!
lines = ['第一行\n', '第二行\n', '第三行\n']
with open('output.txt', 'w', encoding='utf-8') as f:
f.writelines(lines)
\n。这是新手最容易犯的错误,我曾经也在这上面吃过亏。
三、flush() 与缓冲区
这个知识点,说实话,很多人学了 Python 好几年都没搞明白。但一旦你遇到「数据写进去了,文件里却没有」这种诡异问题,你就知道它有多重要了。
缓冲区是什么?
Python 写文件时,不会立刻把数据写到硬盘上。它会先攒在一个「缓冲区」里,等缓冲区满了或者文件关闭了,才一次性写入。这样做是为了提高性能——毕竟硬盘读写比内存慢得多。
# 看看缓冲区在干嘛
with open('test.txt', 'w', encoding='utf-8') as f:
f.write('数据写进去了吗?')
# 此时数据还在缓冲区,没到硬盘
# 如果程序在这里崩溃,数据就丢了
f.flush() # 强制把缓冲区的内容写入硬盘
# 现在数据安全了
flush() 后,问题立刻解决。
什么时候需要 flush()?
- 实时监控日志:希望日志立刻写入文件,方便 tail -f 查看
- 程序可能崩溃:在关键数据写入后立即 flush(),防止丢失
- 交互式程序:用户输入后需要立即保存
缓冲区大小怎么调?
open() 函数的 buffering 参数可以控制缓冲区行为:
| buffering 值 | 行为 |
|---|---|
| -1 | 默认缓冲区大小(通常是 4096 或 8192 字节) |
| 0 | 无缓冲(立刻写入,但性能差) |
| 1 | 行缓冲(遇到换行符就写入) |
| >1 | 指定缓冲区大小(字节数) |
# 行缓冲模式:每写一行就刷一次
with open('test.txt', 'w', buffering=1, encoding='utf-8') as f:
f.write('第一行\n') # 立刻写入
f.write('第二行\n') # 立刻写入
# 无缓冲模式:不推荐,除非你不在乎性能
with open('test.txt', 'w', buffering=0, encoding='utf-8') as f:
f.write('立刻写入')
flush() 或行缓冲。别为了省事把所有文件都设成无缓冲,性能会差很多。
四、综合示例:一个简单的日志记录器
把今天学的知识串起来,写一个实用的日志记录器:
class SimpleLogger:
def __init__(self, filename, buffering=1):
self.filename = filename
self.file = open(filename, 'a', buffering=buffering, encoding='utf-8')
def log(self, message):
from datetime import datetime
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
line = f'[{timestamp}] {message}\n'
self.file.write(line)
# 实时日志,立刻刷到硬盘
self.file.flush()
def close(self):
self.file.close()
# 使用示例
logger = SimpleLogger('app.log')
logger.log('程序启动')
logger.log('用户登录成功')
logger.log('发生异常:文件未找到')
logger.close()
这个例子用到了 write()、flush()、行缓冲模式。你想想看,如果不用 flush(),程序突然崩溃,最后几条日志就丢了。嗯,这就是实战中必须考虑的问题。
总结一下
- 读文件:小文件用
read(),大文件逐行用for line in f,需要随机访问用readlines() - 写文件:单行用
write(),批量用writelines(),别忘了加\n - 缓冲区:默认有缓冲,需要实时写入时用
flush()或行缓冲
说白了,文件读写就是「内存和硬盘之间的搬运工」。你掌握了这些方法,以后处理配置文件、日志文件、数据导出,都能游刃有余。我当年要是有人这么给我讲,也不至于在缓冲区上栽跟头了。