文件不过是带路径的字符串
大多数程序迟早都要读写文件:配置、用户数据、日志、CSV 导出、小型缓存。Python 让这件事很简单:只需要一个内置函数 open(),再加一个 with 块确保文件被干净地关闭。
我们来逐一看看常见的情况。
读取整个文件
最简单的读取方式:
with open("notes.txt") as f:
contents = f.read()
print(contents)
open("notes.txt") 以读取模式(默认模式)打开文件。with 块意味着离开这个块时 Python 会关闭文件,即使抛出了异常也一样。文件关闭后就不能再用 f 了,文本内容保存在 contents 中。
contents 是包含整个文件内容的一个字符串。小文件这样读没问题,2 GB 的日志就不行了。
逐行读取
对于较大的文件,应该逐行遍历,而不是把所有内容都读进内存:
with open("big.log") as f:
for line in f:
if "ERROR" in line:
print(line.strip())
每次迭代得到一行,包括行末的换行符,所以 .strip() 才会出现得这么频繁。如果你想要一个由各行组成的列表,可以用 f.readlines(),不过通常直接遍历才是你想要的。
写入文件
要写入文件,把模式作为第二个参数传进去:
with open("output.txt", "w") as f:
f.write("first line\n")
f.write("second line\n")
有两点需要注意:
"w"会覆盖文件。 如果output.txt已经存在,open一运行,原来的内容就没了。- 换行符要自己加。
f.write("hello")只会写入这五个字符,末尾没有换行。
如果要追加而不是覆盖:
with open("output.txt", "a") as f:
f.write("another line\n")
也可以用 writelines 一次写入多行:
lines = ["a\n", "b\n", "c\n"]
with open("letters.txt", "w") as f:
f.writelines(lines)
规则一样:换行符由你自己提供。
用 print 写入文件
print 有一个 file 参数,用它写文件就不用手动加换行符了:
with open("log.txt", "w") as f:
print("started", file=f)
print("finished", file=f)
每次 print 都会写入它的参数,再加上一个换行符。对于随手输出的内容,这往往是最顺手的写法。
文本模式与二进制模式
open 默认使用文本模式。Python 会把字节解码成字符串(在现代平台上默认用 UTF-8),并处理换行符的转换。
对于图片、PDF、编译后的文件,或者任何不是文本的内容,在模式中加上 "b",以二进制模式打开:
with open("image.png", "rb") as f:
data = f.read()
print(len(data)) # number of bytes
with open("image_copy.png", "wb") as f:
f.write(data)
在二进制模式下,你得到的是 bytes 对象而不是 str,Python 也不会改动行尾。
指定编码
对于文本文件,要明确写出编码。在 2026 年,UTF-8 几乎总是正确的选择:
with open("notes.txt", encoding="utf-8") as f:
contents = f.read()
如果你处理的是来自只用 Windows 的老系统的旧文件,可能还会遇到 cp1252 或 latin-1。编码猜错了,就会得到乱码或者 UnicodeDecodeError。
pathlib:处理路径的现代方式
标准库的 pathlib 模块提供了比原始字符串更顺手的 API:
from pathlib import Path
path = Path("notes.txt")
# Simple read and write.
contents = path.read_text(encoding="utf-8")
path.write_text("new contents\n", encoding="utf-8")
# Build paths without worrying about / vs \.
data_dir = Path("data")
log_path = data_dir / "today.log"
print(log_path)
print(log_path.exists())
print(log_path.suffix) # ".log"
print(log_path.stem) # "today"
print(log_path.parent) # "data"
当你只想一次性读写全部内容时,Path.read_text() 和 write_text() 是 with open(...) as f 写法的快捷方式。如果要逐行遍历,仍然要用 open 或 path.open()。
一个完整的小例子
从文件中读取一组条目,过滤后把结果写入另一个文件:
from pathlib import Path
source = Path("items.txt")
destination = Path("filtered.txt")
items = source.read_text().splitlines()
kept = [item for item in items if item and not item.startswith("#")]
destination.write_text("\n".join(kept) + "\n")
print(f"Kept {len(kept)} items out of {len(items)}")
splitlines() 是和 join 配套的按行操作方法。它按换行符拆分字符串,但不保留换行符。如果你之后反正要把各部分重新拼起来,它就很方便。
出错的时候
打开一个不存在的文件会抛出 FileNotFoundError。读取一个没有权限的文件会抛出 PermissionError。这两个都是 OSError 的子类,而 OSError 又是 Exception 的子类。我们会在下一页详细讲如何正确处理异常。
现在先简单预览一下错误处理的样子:
from pathlib import Path
path = Path("maybe.txt")
try:
contents = path.read_text()
except FileNotFoundError:
print("File doesn't exist - starting fresh.")
contents = ""
print(repr(contents))
关键习惯
- 始终用
with open(...),不要用原始的open再手动关闭。 - 文本文件始终传入
encoding=,默认用 UTF-8。 - 大文件逐行遍历,不要用
.read()一次读完。 - 处理路径和常见的读写操作时,用
pathlib,能帮你省去大量字符串拼接的麻烦。
常见问题
Python 中怎么读取文件?
把 open() 和 with 语句一起使用,用完后它会自动关闭文件。with open('file.txt') as f: contents = f.read() 会把整个文件读成一个字符串。如果要逐行读取,直接遍历文件对象:for line in f:。
打开模式 'r'、'w' 和 'a' 有什么区别?
'r' 是读取(默认模式)。'w' 是写入:文件不存在时创建它,存在时清空它。'a' 是追加:在文件末尾添加内容,不会删掉已有内容。加上 'b' 是二进制模式,加上 '+' 是读写模式。
为什么要用 with open 而不是直接用 open?
with 语句能保证即使中途出错,文件也会被关闭。不用 with 的话,你得自己调用 .close(),还要记得在出错的分支里也调用。with 更安全,代码也更少。