Python 文件操作:读写文件这件事,记住 with open 就够了
写程序,文件操作避不开。读配置、写日志、处理数据、导出报表——天天都在跟文件打交道。
Python 的文件操作其实就一件事:用 with open() 打开文件,然后读或写。语法很简单,但实际用的时候有些细节——编码、大文件、异常处理——不注意就会出问题。
核心原则:永远用 with open
1 2 3 4 5 6 7 8 9
| with open("data.txt", "r", encoding="utf-8") as f: content = f.read()
f = open("data.txt", "r") content = f.read() f.close()
|
with 语句会在代码块结束后自动关闭文件,即使中间抛异常也一样。记住这一点,就不用操心资源泄露的问题了。
读文件:三种方式,按需取用
1. 小文件:直接读完
1 2
| with open("config.json", "r", encoding="utf-8") as f: content = f.read()
|
文件不大的时候,这是最直接的写法。几 MB 以内的文件都没问题。
2. 大文件:逐行读,不占内存
1 2 3 4 5
| with open("large_log.txt", "r", encoding="utf-8") as f: for line in f: if "ERROR" in line: print(line.strip())
|
for line in f 是逐行读取,不会一次性把所有内容加载到内存。处理几 GB 的日志文件就用这个方式。
3. 读所有行到列表
1 2 3
| with open("data.txt", "r", encoding="utf-8") as f: lines = f.readlines()
|
注意 readlines() 会把所有行都读进内存,适合小文件。大文件还是用上面的逐行遍历。
写文件:覆盖还是追加
1 2 3 4 5 6 7 8
| with open("output.txt", "w", encoding="utf-8") as f: f.write("第一行\n") f.write("第二行\n")
with open("output.txt", "a", encoding="utf-8") as f: f.write("第三行\n")
|
写多行:
1 2 3
| lines = ["第一行\n", "第二行\n", "第三行\n"] with open("output.txt", "w", encoding="utf-8") as f: f.writelines(lines)
|
writelines() 不会自动加换行,需要自己在字符串里带上 \n。
编码:别偷懒,每次都指定 utf-8
1 2 3 4 5 6 7
| with open("data.txt", "r", encoding="utf-8") as f: content = f.read()
with open("data.txt", "r") as f: content = f.read()
|
Windows 上默认编码可能是 gbk,Linux 上是 utf-8。 不指定编码的话,同样的代码在不同环境表现不一样。每次都写 encoding="utf-8" 是最省事的做法。
如果遇到 UnicodeDecodeError,说明文件编码不是 utf-8,换成其他编码试试:
1 2 3 4 5 6
| try: with open("data.txt", "r", encoding="utf-8") as f: content = f.read() except UnicodeDecodeError: with open("data.txt", "r", encoding="gbk") as f: content = f.read()
|
二进制文件:图片、音频、视频
读二进制文件用 'rb',写用 'wb',不需要指定编码。
1 2 3 4 5 6 7 8 9
| with open("image.jpg", "rb") as f: data = f.read() print(f"文件大小:{len(data)} 字节")
with open("source.jpg", "rb") as src: with open("copy.jpg", "wb") as dst: dst.write(src.read())
|
复制大文件不要一次性读完,应该分块读写:
1 2 3 4 5 6 7 8
| chunk_size = 8192 with open("source.iso", "rb") as src: with open("copy.iso", "wb") as dst: while True: chunk = src.read(chunk_size) if not chunk: break dst.write(chunk)
|
文件存在性判断和目录操作
判断文件/目录是否存在:
1 2 3 4 5 6 7 8 9
| from pathlib import Path
p = Path("data.txt")
if p.exists(): if p.is_file(): print("是文件") elif p.is_dir(): print("是目录")
|
创建目录:
1 2 3 4 5 6 7
| from pathlib import Path
Path("data").mkdir(exist_ok=True)
Path("data/2026/logs").mkdir(parents=True, exist_ok=True)
|
exist_ok=True 的意思是”目录存在也不报错”,省得每次都要先判断。
遍历目录:
1 2 3 4 5 6 7 8 9
| from pathlib import Path
for file in Path(".").glob("*.txt"): print(file)
for file in Path(".").rglob("*.log"): print(file)
|
os 模块的老写法也常见:
1 2 3 4 5 6
| import os
os.path.exists("data.txt") os.path.isdir("data") os.makedirs("data/logs", exist_ok=True) os.listdir(".")
|
pathlib 是更新的写法,更直观;os 是老写法,老代码里常见。两种都认识就行,新代码推荐用 pathlib。
CSV 文件:用 csv 模块,别自己 split
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import csv
with open("data.csv", "r", encoding="utf-8", newline="") as f: reader = csv.reader(f) header = next(reader) for row in reader: print(row)
rows = [ ["姓名", "年龄", "城市"], ["张三", 25, "北京"], ["李四", 30, "上海"], ] with open("output.csv", "w", encoding="utf-8", newline="") as f: writer = csv.writer(f) writer.writerows(rows)
|
newline="" 是为了避免 Windows 上出现多余的空行,写 CSV 的时候加上就对了。
JSON 文件:用 json 模块
1 2 3 4 5 6 7 8 9 10
| import json
with open("config.json", "r", encoding="utf-8") as f: config = json.load(f)
data = {"name": "张三", "age": 25} with open("output.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2)
|
ensure_ascii=False 保留中文,indent=2 让输出可读。这两个参数记不住的话,直接复制用就行。
异常处理:文件操作容易出错的地方
文件操作常见的异常就那么几种,处理好了程序就不会崩:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from pathlib import Path
file_path = Path("data.txt")
try: with open(file_path, "r", encoding="utf-8") as f: content = f.read() except FileNotFoundError: print(f"文件不存在:{file_path}") except PermissionError: print(f"没有读取权限:{file_path}") except UnicodeDecodeError: print(f"编码错误:{file_path}") except Exception as e: print(f"其他错误:{e}")
|
实际写代码的时候,至少把 FileNotFoundError 处理了,不然用户看到的就是一屏幕红字堆栈。
临时文件:用完自动删
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import tempfile from pathlib import Path
with tempfile.NamedTemporaryFile(mode="w+", encoding="utf-8", delete=False) as f: f.write("临时数据") f.seek(0) print(f.read())
with tempfile.TemporaryDirectory() as temp_dir: temp_file = Path(temp_dir) / "temp.txt" temp_file.write_text("临时文件")
|
适合测试场景或者需要暂存中间数据的场景。