fileinput 模块通过重命名备份+重写实现“就地修改”假象,需设 inplace=True 并用 print() 输出每行;中小文件可读入列表修改后整体写回;大文件应流式处理或调用 sed 等外部命令。

用 fileinput 边读边改,最接近“直接修改”的原地操作
Python 没有真正意义上的“随机写入某一行”API,因为文件是字节流,行长度不固定,插入或删减会牵动后续所有偏移。但 fileinput 模块提供了一种伪装成“就地修改”的安全方式:它实际是把原文件重命名为备份(如 filename.bak),再把处理后的内容写入原路径——对调用者来说,就像直接改了原文件。
关键点在于:必须用 inplace=True 参数,并在循环中用 print() 输出你想要的行内容(包括未变的行)。
- 不加
inplace=True就只是标准输入流,不会写回文件 - 忘记
print()当前行,那一行就丢了(被跳过) - 想改第 5 行?用
enumerate(fileinput.input(...))数行号,注意行号从 0 开始
import fileinput
<p>for i, line in enumerate(fileinput.input("data.txt", inplace=True)):
if i == 4: # 第 5 行(0-indexed)
print("new content for line 5")
else:
print(line, end='') # 注意保留原换行,line 已含 \n
</p>
用列表缓存全文件再写回,适合中小文件且需多次判断
当你要按内容匹配(比如包含某个关键词)、或需要前后行联动判断时,fileinput 的单次流式处理就不够用了。这时更稳妥的做法是:一次性读入所有行到内存,修改后整体写回。虽然不是“原地”,但对大多数日志、配置类文本足够快。
注意:别用 readlines() 后直接 list[index] = ... 然后 writelines() —— 因为 readlines() 保留每行末尾的 \n,而 writelines() 不自动补换行,容易导致最后一行粘连。
- 推荐用
with open(...) as f: lines = f.readlines()读取 - 修改
lines[n]时,确保新值以\n结尾(或统一用lines[n] = new_line + "\n") - 写回时用
f.writelines(lines)即可,不用手动加换行 - 文件超 100MB 就该考虑流式处理,避免内存爆掉
用 sed 或系统命令快速替代(Linux/macOS)
如果只是简单替换某一行(比如第 N 行),且运行环境可控(非 Windows),调用系统 sed 反而最快,不加载整个文件进 Python。
例如把 config.txt 第 3 行替换成 timeout=30:
import os
os.system("sed -i '3s/.*/timeout=30/' config.txt")
注意:-i 在 macOS 上必须带空字符串参数(-i ''),否则报错;Linux 则不需要。跨平台脚本里得先判断系统,不如老实用 Python 做。
- Windows 没原生
sed,得装 Git Bash 或用 PowerShell 的(Get-Content).Replace(),反而更复杂 -
sed对正则特殊字符(如/、&)要转义,不如 Python 字符串操作直观 - 它不校验文件是否存在或是否可写,出错静默,调试困难
为什么不能用 seek() 定位并 write() 修改某一行?
这是新手常踩的坑:看到文件对象有 seek(),就想“算出第 n 行开头位置,然后 seek() 过去写”。但问题在于——
- 文本文件没有“行地址索引”,必须从头逐字节扫描
\n才能定位第 n 行起始,这和“快速”背道而驰 - 即使你算准了位置,写入新内容长度 ≠ 原行长度时,会覆盖后续内容(写短了留垃圾,写长了挤掉后面行)
- 二进制模式下
seek()虽可行,但你得自己处理编码、换行符、缓冲区刷新,几乎等于重写一个文本编辑器
所以,所谓“快速定位某一行并修改”,本质是权衡:要么接受重写整个文件(安全通用),要么依赖外部工具(快但不跨平台),没有银弹。真正的大文件(GB 级)需要分块读取+临时文件拼接,那就超出“快速”范畴了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











