
前阵子有个刚学 Python 的朋友问我:写日志、读配置、把一个文件夹里的文件批量归类……这些活儿到底怎么用代码搞定?他翻了一圈教程,看的时候都懂,一到自己写就卡壳。
其实文件操作是 Python 里性价比最高的一块技能。不管你是想写个自动整理下载文件夹的小脚本,还是做数据处理、爬虫落地、日志系统,几乎都绕不开"读文件、写文件"这两件事。而且 Python 把这事做得特别友好,几行代码就能跑起来。
这篇我不堆概念,直接按"你真正会遇到的场景"来讲,每一段代码都能复制粘贴就用。从最基础的打开文件,到 JSON/CSV、异常处理,最后拼一个能直接部署的自动归档脚本。跟着抄一遍,这块你就通关了。
先记一张表:打开文件的几种"模式"
所有文件操作的起点,都是 open() 这个函数。它最关键的第二个参数叫"模式",决定了你是来读的、写的、还是追加的。新手最容易在这里翻车——比如本来想追加,结果用了 "w",把原文件整个清空了。
先把这张速查表记住,后面就不慌了:
模式 含义 文件不存在时 文件已存在时──── ──── ────────── ──────────"r" 读取(默认) ❌ 报错 正常读"w" 写入 ✅ 新建 ⚠️ 清空重写!"a" 追加 ✅ 新建 末尾接着写"rb" 二进制读 ❌ 报错 读图片/PDF等"wb" 二进制写 ✅ 新建 写图片/PDF等
划重点:**"w" 会把原文件内容全部清空再写**。想保留旧内容、只在后面加东西,一定要用 "a"(append,追加)。这个坑几乎每个新手都踩过一次。
打开文件,请一律用 with
最原始的写法是这样,打开、用完、手动关闭:
file = open("example.txt", "r")content = file.read()print(content)file.close() # 必须记得关,否则文件句柄泄漏
能用,但不推荐。因为一旦中间代码报错,close() 就执行不到了,文件一直占着。所以现代 Python 一律用 with 写法——它会在代码块结束时自动帮你关文件,哪怕中途报错也照关不误:
with open("example.txt", "r", encoding="utf-8") as file: content = file.read() print(content)# 出了这个缩进块,文件已经自动关好了
注意我多写了个 encoding="utf-8"。这是给国内开发者的头号避坑提醒:不写编码,遇到中文很容易报 UnicodeDecodeError 或者读出一堆乱码,尤其在 Windows 上。**养成习惯,读写文本文件都带上 encoding="utf-8"**,能省掉你 90% 的中文乱码问题。
读文件的三种姿势,按文件大小选
姿势一:小文件,一口气全读进来。 适合配置文件、小文本:
with open("story.txt", "r", encoding="utf-8") as file: text = file.read() # 整个文件读成一个字符串print(text)
姿势二:只读开头几行。 用 readline(),一次读一行:
with open("story.txt", "r", encoding="utf-8") as file: first = file.readline() second = file.readline()print(first, second)
姿势三:大文件,一行一行地读(重要)。 直接 for line in file 循环,Python 不会把整个文件塞进内存,而是读一行处理一行。几个 G 的日志文件也能轻松扛住:
with open("story.txt", "r", encoding="utf-8") as file:for line in file: print(line.strip()) # strip() 去掉每行末尾的换行符
strip() 这一下别省,不然每行后面都会多一个空行。记住原则:文件大就别用 read() 一次性读,改用逐行循环。
写文件:分清"覆盖"和"追加"
写入就是刚才那张表的实战。"w" 覆盖写,文件不存在会自动创建:
with open("notes.txt", "w", encoding="utf-8") as file: file.write("Python 文件操作很简单。\n") file.write("多练几遍就熟了。")
\n 是换行符,不写的话两句会挤在一行。
追加用 "a",这是写日志的标准姿势——每次运行都往后接,不会覆盖之前的记录:
with open("logs.txt", "a", encoding="utf-8") as file: file.write("新的一条日志\n")
一次写很多行,可以用 writelines()(注意它不会自动加换行,得自己在每行带上 \n):
lines = ["第一行\n", "第二行\n", "第三行\n"]with open("output.txt", "w", encoding="utf-8") as file: file.writelines(lines)
二进制文件:图片、PDF、视频怎么处理
文本以外的东西——图片、PDF、视频、模型文件——都算"二进制文件",模式要带 b,而且不能带 encoding(二进制没有编码这一说)。
复制一张图片,本质就是"二进制读出来,再二进制写进去":
with open("photo.jpg", "rb") as source: data = source.read()with open("copy.jpg", "wb") as destination: destination.write(data)
这几行就是一个最朴素的文件复制器,换成 PDF、mp4 一样能用。
路径处理:pathlib 比 os 更好用
处理"文件在哪、存不存在、建文件夹"这类事,老办法是用 os 模块:
import osprint(os.getcwd()) # 当前工作目录print(os.path.exists("data.txt")) # 文件在不在os.makedirs("project/data/files", exist_ok=True) # 建多层文件夹# 挑出当前目录下所有 txt 文件for name in os.listdir("."):if name.endswith(".txt"): print(name)
(小提示:makedirs 加上 exist_ok=True,文件夹已存在时就不会报错,省一层判断。)
不过更现代、更推荐的是 pathlib,它把路径当成对象来操作,读起来像说人话:
from pathlib import Pathpath = Path("hello.txt")print(path.exists()) # 存在吗print(path.name) # 文件名 hello.txtprint(path.suffix) # 后缀 .txt# 读写更是一行搞定path.write_text("你好,pathlib", encoding="utf-8")content = path.read_text(encoding="utf-8")print(content)
新项目建议直接上 pathlib,代码更干净,跨平台也更省心(不用手动拼 / 还是 \)。
JSON:存配置、接口数据的标配
写后端、对接接口,天天跟 JSON 打交道。Python 内置 json 模块,一个存一个读:
import jsonuser = {"name": "小明", "age": 25, "is_admin": False}# 写:dump 把对象存成 JSON 文件with open("user.json", "w", encoding="utf-8") as file: json.dump(user, file, indent=4, ensure_ascii=False)
这里又有一个国内必踩的坑:ensure_ascii=False。不加它,中文会被存成 小明 这种鬼样子;加上之后,文件里就是清清爽爽的"小明"。indent=4 则是让 JSON 带缩进、好看好读。
读回来用 load:
import jsonwith open("user.json", "r", encoding="utf-8") as file: data = json.load(file)print(data["name"]) # 小明
CSV:数据分析和报表的日常
导数据、做报表离不开 CSV。写入用 csv.writer,注意打开时要加 newline="",否则 Windows 上会多出空行:
import csvwith open("employees.csv", "w", newline="", encoding="utf-8-sig") as file: writer = csv.writer(file) writer.writerow(["姓名", "部门", "薪资"]) writer.writerow(["张三", "研发", 80000]) writer.writerow(["李四", "市场", 65000])
这里用了 utf-8-sig 而不是普通 utf-8——这样用 Excel 打开才不会中文乱码,又一个实战细节。
读的时候,比起 csv.reader 返回一行行列表,我更推荐 DictReader,它把每行读成字典,用列名取值,可读性好太多:
import csvwith open("employees.csv", "r", encoding="utf-8-sig") as file: reader = csv.DictReader(file)for row in reader: print(row["姓名"], row["薪资"]) # 按列名取,不用记第几列
异常处理:文件操作必须包一层
文件不一定存在、也可能没权限、内容还可能是坏的。所以正经项目里,文件操作几乎都要用 try/except 兜底,不然一报错整个程序就挂了:
try:with open("data.txt", "r", encoding="utf-8") as file: print(file.read())except FileNotFoundError: print("文件不存在")except PermissionError: print("没有权限访问")except Exception as error: print("其他意外错误:", error)
只要数据来源不完全可控(用户上传的、别的程序生成的),就老实包上这层保险。裸奔一时爽,线上出问题就得半夜爬起来修。
实战:把上面的拼成三个能用的小工具
光看片段没感觉,串起来才叫本事。这三个是我自己也常写的。
① 一个简单的日志器——记录发生了什么、什么时候发生的:
from datetime import datetimedeflog(message):with open("app.log", "a", encoding="utf-8") as file: file.write(f"{datetime.now()} - {message}\n")log("用户登录成功")log("开始处理订单")
② 大文件生成器——处理几个 G 的文件也不爆内存,一行一行往外"吐":
defread_large_file(filename):with open(filename, "r", encoding="utf-8") as file:for line in file:yield line.strip() # 用 yield,读一行给一行,不囤内存for line in read_large_file("big_data.txt"): print(line)
③ 自动归档脚本——这是文件操作最爽的应用:扫描一个文件夹,把处理完的 txt 自动搬到归档目录。定时跑一下,告别手动整理:
import shutilfrom pathlib import Pathsource = Path("incoming") # 待处理文件夹archive = Path("archive") # 归档文件夹archive.mkdir(exist_ok=True) # 归档目录不存在就建一个for file in source.glob("*.txt"): # 只挑 txt 文件 shutil.move(str(file), archive / file.name) print(f"已归档:{file.name}")
想更进一步,配合压缩也很简单,zipfile 一把打包:
import zipfilewith zipfile.ZipFile("archive.zip", "w") as zf: zf.write("notes.txt")# 解压with zipfile.ZipFile("archive.zip", "r") as zf: zf.extractall("extracted")
新手最容易栽的 4 个坑,收好
把这篇最该记的避坑点单独拎出来,贴墙上都行:
- 中文乱码:读写文本一律带
encoding="utf-8";写给 Excel 看的 CSV 用 utf-8-sig;json.dump 记得 ensure_ascii=False。 "w" 会清空文件:想保留旧内容只加新的,用 "a" 追加,别手滑写成 "w"。- **大文件别
read()**:几百 M 以上一次性读会卡死甚至爆内存,改用 for line in file 逐行读。 - 相对路径看"当前目录":脚本里写
"data.txt" 找的是运行时所在目录,不是脚本所在目录,容易"文件明明在却找不到"。拿不准就用 Path(__file__).parent 定位脚本目录。
文件操作就是这么个东西——单看每个片段都简单,但它是几乎所有真实项目的地基。写日志靠它、存配置靠它、数据落地靠它、自动化脚本更是离不开它。你今天把这 20 多个片段抄一遍、跑通一遍,以后遇到"读个文件、写个文件"的活儿,基本不用再翻文档了。
互动一下: 这些片段你跑通了吗?留个小作业——试试用文中的"自动归档脚本"改一版,把你下载文件夹里的图片自动按格式分类到不同子文件夹。写出来的同学,把代码贴评论区,我挑几个写得漂亮的置顶展示 👀 卡住了也别憋着,评论区问,我看到就回。