首页 / 知识库 / 0基础入门-阅读资料 / 0基础-python入门到精通

第 9 章 文件与 I/O 操作

9.1 文件打开模式

open() 是 Python 中操作文件的核心函数:

f = open("example.txt", mode="r", encoding="utf-8")
content = f.read()
f.close()   # 别忘了关闭!
模式说明文件不存在时
r只读(默认)报错
w写入(清空原内容)创建
a追加创建
x排他创建(文件已存在则报错)创建
b二进制模式(与上面组合:rb, wb
t文本模式(默认,与上面组合:rt, wt
+读写模式(r+, w+, a+
# 常见组合
open("f.txt", "r")    # 读取文本(默认)
open("f.txt", "w")    # 写入文本(覆盖)
open("f.txt", "a")    # 追加文本
open("f.txt", "r+")   # 读写文本
open("f.bin", "rb")   # 读取二进制
open("f.bin", "wb")   # 写入二进制
open("f.txt", "x")    # 创建新文件(已存在则报错)

9.2 上下文管理器与 with 语句

with 语句确保文件自动关闭,即使发生异常:

# 推荐写法
with open("example.txt", "r", encoding="utf-8") as f:
    content = f.read()
# 离开 with 块后,文件自动关闭

# 等价于
f = open("example.txt", "r", encoding="utf-8")
try:
    content = f.read()
finally:
    f.close()

# 同时打开多个文件
with open("input.txt") as fin, open("output.txt", "w") as fout:
    for line in fin:
        fout.write(line.upper())

# Python 3.10+ 可以用括号换行
with (
    open("input.txt") as fin,
    open("output.txt", "w") as fout,
):
    pass

9.3 文本文件读写与编码

读取

# 一次性读取全部内容
with open("data.txt", encoding="utf-8") as f:
    content = f.read()        # 字符串
    print(len(content))

# 按行读取为列表
with open("data.txt", encoding="utf-8") as f:
    lines = f.readlines()     # 列表,每行含 \n
    lines = [line.rstrip("\n") for line in lines]

# 逐行读取(最省内存)
with open("data.txt", encoding="utf-8") as f:
    for line in f:            # 文件对象本身就是迭代器
        line = line.rstrip("\n")
        print(line)

# 读取指定字节数
with open("data.txt", encoding="utf-8") as f:
    chunk = f.read(100)       # 读 100 个字符
    rest = f.read()           # 读取剩余所有

写入

# 写入(覆盖)
with open("output.txt", "w", encoding="utf-8") as f:
    f.write("第一行\n")
    f.write("第二行\n")

# 写入多行
lines = ["line 1", "line 2", "line 3"]
with open("output.txt", "w", encoding="utf-8") as f:
    f.writelines(line + "\n" for line in lines)

# 追加
with open("log.txt", "a", encoding="utf-8") as f:
    f.write("新的日志条目\n")

# print 也能写入文件
with open("output.txt", "w", encoding="utf-8") as f:
    print("Hello, World!", file=f)
    print("Second line", file=f)

编码

# 指定编码(强烈推荐总是显式指定)
with open("chinese.txt", "w", encoding="utf-8") as f:
    f.write("你好世界")

# 读取 GBK 编码的文件
with open("old_file.txt", "r", encoding="gbk") as f:
    content = f.read()

# 处理编码错误
with open("mixed.txt", "r", encoding="utf-8", errors="ignore") as f:
    content = f.read()  # 忽略无法解码的字节

with open("mixed.txt", "r", encoding="utf-8", errors="replace") as f:
    content = f.read()  # 用 ? 替换无法解码的字节

# 转码:GBK → UTF-8
with open("gbk_file.txt", "r", encoding="gbk") as fin:
    content = fin.read()
with open("utf8_file.txt", "w", encoding="utf-8") as fout:
    fout.write(content)

9.4 二进制文件读写

# 读取二进制文件
with open("image.png", "rb") as f:
    data = f.read()        # bytes 对象
    print(type(data))      # <class 'bytes'>
    print(data[:8])        # PNG 文件头

# 写入二进制文件
with open("copy.png", "wb") as f:
    f.write(data)

# 分块读取大文件
def copy_file(src, dst, chunk_size=8192):
    with open(src, "rb") as fin, open(dst, "wb") as fout:
        while chunk := fin.read(chunk_size):
            fout.write(chunk)

# 文件指针操作
with open("data.bin", "rb") as f:
    f.seek(10)          # 移动到第 10 个字节
    data = f.read(4)    # 读 4 个字节
    pos = f.tell()      # 当前位置:14
    f.seek(0)           # 回到开头
    f.seek(-10, 2)      # 从末尾倒数 10 字节(2 表示从末尾算)

9.5 pathlib 模块:面向对象的路径操作

pathlib 是 Python 3.4+ 引入的现代路径处理方式,比 os.path 更优雅:

from pathlib import Path

# 创建路径
p = Path("data/files/report.txt")
home = Path.home()               # 用户主目录
cwd = Path.cwd()                 # 当前工作目录

# 路径拼接(用 / 运算符)
config = Path.home() / ".config" / "myapp" / "settings.json"

# 路径属性
p = Path("/home/user/data/report.csv")
p.name           # "report.csv"
p.stem           # "report"
p.suffix         # ".csv"
p.suffixes       # [".csv"](多后缀如 .tar.gz → ['.tar', '.gz'])
p.parent         # Path("/home/user/data")
p.parents[1]     # Path("/home/user")
p.parts          # ('/', 'home', 'user', 'data', 'report.csv')
p.is_absolute()  # True

文件操作

# 读写文件(简化版)
p = Path("example.txt")
p.write_text("Hello, World!", encoding="utf-8")
content = p.read_text(encoding="utf-8")

p = Path("data.bin")
p.write_bytes(b"\x00\x01\x02")
data = p.read_bytes()

# 判断
p.exists()       # 是否存在
p.is_file()      # 是否是文件
p.is_dir()       # 是否是目录
p.is_symlink()   # 是否是符号链接

# 文件信息
stat = p.stat()
stat.st_size     # 文件大小(字节)
stat.st_mtime    # 修改时间(时间戳)

目录操作

d = Path("project")

# 创建目录
d.mkdir(exist_ok=True)                      # 单层
d.mkdir(parents=True, exist_ok=True)        # 递归创建

# 遍历目录
for item in Path(".").iterdir():
    print(item.name, "目录" if item.is_dir() else "文件")

# glob 模式匹配
for py_file in Path("src").glob("*.py"):
    print(py_file)

for py_file in Path("src").rglob("*.py"):  # 递归搜索
    print(py_file)

# 实用示例:查找项目中所有 Python 文件并统计行数
total_lines = 0
for py_file in Path(".").rglob("*.py"):
    lines = py_file.read_text(encoding="utf-8").splitlines()
    total_lines += len(lines)
    print(f"{py_file}: {len(lines)} 行")
print(f"总计: {total_lines} 行")

路径操作

p = Path("data/raw/report.csv")

# 修改文件名
p.with_name("summary.csv")      # Path("data/raw/summary.csv")
p.with_stem("summary")          # Path("data/raw/summary.csv") (3.9+)
p.with_suffix(".txt")           # Path("data/raw/report.txt")

# 解析为绝对路径
p.resolve()                      # Path("/abs/path/data/raw/report.csv")

# 相对路径
p.relative_to("data")           # Path("raw/report.csv")

# 删除
p.unlink(missing_ok=True)       # 删除文件
d.rmdir()                        # 删除空目录

9.6 序列化:json、pickle、shelve

json — 通用数据交换格式

import json

data = {
    "name": "Alice",
    "age": 30,
    "hobbies": ["reading", "coding"],
    "address": {"city": "Beijing", "zip": "100000"}
}

# 对象 → JSON 字符串
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)

# JSON 字符串 → 对象
parsed = json.loads(json_str)

# 写入文件
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 从文件读取
with open("data.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)

JSON 类型映射

PythonJSON
dictobject
list, tuplearray
strstring
int, floatnumber
True/Falsetrue/false
Nonenull

自定义序列化

from datetime import datetime

class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

data = {"created": datetime.now()}
json.dumps(data, cls=DateEncoder)
# '{"created": "2026-03-31T10:30:00.123456"}'

pickle — Python 对象序列化

import pickle

data = {"name": "Alice", "scores": [90, 85, 92]}

# 序列化
with open("data.pkl", "wb") as f:
    pickle.dump(data, f)

# 反序列化
with open("data.pkl", "rb") as f:
    loaded = pickle.load(f)

# pickle 可以序列化几乎任何 Python 对象(函数、类等)
# 但注意:不要 unpickle 不受信任的数据(安全风险)

shelve — 简易的持久化字典

import shelve

with shelve.open("mydata") as db:
    db["users"] = ["Alice", "Bob"]
    db["config"] = {"theme": "dark"}

with shelve.open("mydata") as db:
    print(db["users"])    # ['Alice', 'Bob']
    print(list(db.keys()))  # ['users', 'config']

9.7 StringIO 与 BytesIO

在内存中创建文件对象,用于需要文件接口但不想写磁盘的场景:

from io import StringIO, BytesIO

# StringIO — 内存中的文本文件
sio = StringIO()
sio.write("Hello, ")
sio.write("World!")
content = sio.getvalue()   # "Hello, World!"

# 作为输入源
sio = StringIO("line1\nline2\nline3")
for line in sio:
    print(line.strip())

# BytesIO — 内存中的二进制文件
bio = BytesIO()
bio.write(b"binary data")
data = bio.getvalue()

# 实用:CSV 处理不落盘
import csv

output = StringIO()
writer = csv.writer(output)
writer.writerow(["name", "age"])
writer.writerow(["Alice", 30])
csv_content = output.getvalue()
print(csv_content)
# name,age
# Alice,30

本章小结:文件 I/O 是编程的基础技能。始终使用 with 语句管理文件、始终显式指定编码、用 pathlib 替代 os.path 处理路径、根据需求选择 json/pickle/shelve 进行序列化。掌握这些,你就能自如地处理各种文件操作需求。