Python 文件 IO 与数据序列化:从 CSV 到 Parquet 的完整指南

Python 文件 IO 与数据序列化完全指南:文本/二进制文件、CSV、JSON、YAML、Pickle、MessagePack、Parquet 的读写操作,涵盖 pathlib、with 上下文、编码处理、大数据分块读取等企业级最佳实践。

无论是日志处理、数据导出还是配置管理,文件 IO 和序列化都是 Python 开发者每天面对的任务。本文覆盖从基础文本操作到高性能二进制格式的完整技术栈。


目录

  1. pathlib:现代路径操作
  2. 文本文件读写
  3. 二进制文件与内存映射
  4. CSV 处理
  5. JSON 序列化
  6. YAML 配置
  7. Pickle:Python 原生序列化
  8. MessagePack:二进制 JSON
  9. Parquet:列式存储
  10. 大数据分块读取
  11. 编码处理

1. pathlib:现代路径操作

pathlib 是 Python 3.4+ 引入的面向对象路径库,已取代 os.path

from pathlib import Path
import shutil

# 创建路径对象
p = Path("/Users/alice/data/report.txt")
# 或跨平台写法
p = Path.home() / "data" / "report.txt"

# 路径分解
print(p.name)         # report.txt(文件名)
print(p.stem)         # report(无扩展名)
print(p.suffix)       # .txt(扩展名)
print(p.parent)       # /Users/alice/data(父目录)
print(p.parts)        # ('/', 'Users', 'alice', 'data', 'report.txt')
print(p.exists())     # True / False

# 遍历目录
for item in Path(".").iterdir():
    if item.is_file():
        print(f"文件: {item.name}, 大小: {item.stat().st_size}")

# 递归查找(glob)
py_files = list(Path("src").rglob("*.py"))   # 递归找所有 .py
log_files = list(Path("logs").glob("*.log")) # 只找当前目录

# 文件操作
p = Path("hello.txt")
p.write_text("Hello, World!", encoding="utf-8")
content = p.read_text(encoding="utf-8")

# 创建目录
Path("output/2024/08").mkdir(parents=True, exist_ok=True)

# 复制/移动/删除
shutil.copy("src.txt", "dst.txt")
shutil.move("old.txt", "new.txt")
p.unlink()   # 删除文件
# p.rmdir()  # 删除空目录

2. 文本文件读写

2.1 基础读写

from pathlib import Path

# 写
Path("data.txt").write_text("Line 1\nLine 2\n", encoding="utf-8")

# 读
content = Path("data.txt").read_text(encoding="utf-8")

# 逐行读取
for line in Path("data.txt").read_text(encoding="utf-8").splitlines():
    print(line.strip())

# 大文件:不一次性读入内存
with open("large.txt", "r", encoding="utf-8") as f:
    for line in f:          # 逐行迭代(内存友好)
        process(line)

2.2 追加模式与多种打开模式

# 模式说明
# 'r'  读取(默认)
# 'w'  写入(覆盖)
# 'a'  追加
# 'x'  独占创建(文件存在则报错)
# 'b'  二进制模式
# '+'  读写

with open("log.txt", "a", encoding="utf-8") as f:
    f.write("新日志行\n")

# 同时读写
with open("data.txt", "r+", encoding="utf-8") as f:
    content = f.read()
    f.seek(0)
    f.write("新头部\n" + content)

3. 二进制文件与内存映射

3.1 二进制读写

# 写入二进制
data = b"\x00\x01\x02\x03"
Path("binary.dat").write_bytes(data)

# 读取二进制
raw = Path("binary.dat").read_bytes()

# 处理图片/文件
with open("image.png", "rb") as f:
    header = f.read(8)
    if header.startswith(b"\x89PNG"):
        print("这是一个 PNG 文件")

3.2 内存映射(处理超大文件)

import mmap

# 用内存映射读取大文件(不加载到内存)
with open("huge_file.bin", "rb") as f:
    with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
        # mm 像 bytes 对象一样使用
        print(mm[:100])          # 前 100 字节
        print(mm.find(b"target"))  # 查找(O(n))

4. CSV 处理

4.1 读写 CSV

import csv
from pathlib import Path

# 写 CSV
rows = [
    ["name", "age", "city"],
    ["Alice", 25, "Beijing"],
    ["Bob", 30, "Shanghai"],
]

with open("users.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.writer(f)
    writer.writerows(rows)

# 读 CSV
with open("users.csv", "r", encoding="utf-8-sig") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(f"{row['name']}: {row['age']} 岁")

4.2 CSV 最佳实践

# 大文件分块读取
def read_csv_in_chunks(filepath, chunksize=10000):
    import pandas as pd
    for chunk in pd.read_csv(filepath, chunksize=chunksize):
        yield chunk

# 特定编码(Excel 导出的 CSV 常用 gbk)
# pd.read_csv("file.csv", encoding="gbk")

5. JSON 序列化

5.1 基础操作

import json
from pathlib import Path

data = {
    "name": "Alice",
    "age": 25,
    "skills": ["Python", "Go", "Rust"],
    "address": {"city": "Beijing", "zip": "100000"},
}

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

# 写入文件
Path("user.json").write_text(json_str, encoding="utf-8")

# 反序列化
loaded = json.loads(json_str)
print(loaded["name"])

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

with open("user.json", "r", encoding="utf-8") as f:
    data = json.load(f)

5.2 自定义序列化

import json
from datetime import datetime
from decimal import Decimal

class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, Decimal):
            return float(obj)
        if isinstance(obj, set):
            return list(obj)
        return super().default(obj)

data = {
    "created_at": datetime.now(),
    "price": Decimal("99.99"),
    "tags": {"python", "json"},
}

json_str = json.dumps(data, cls=CustomEncoder, ensure_ascii=False, indent=2)
print(json_str)

6. YAML 配置

# pip install pyyaml
import yaml

config = {
    "app": {"name": "MyApp", "debug": False},
    "database": {"host": "localhost", "port": 5432},
}

# 写 YAML
with open("config.yaml", "w", encoding="utf-8") as f:
    yaml.dump(config, f, allow_unicode=True, sort_keys=False)

# 读 YAML
with open("config.yaml", "r", encoding="utf-8") as f:
    loaded = yaml.safe_load(f)

print(loaded["database"]["host"])

7. Pickle:Python 原生序列化

import pickle
from pathlib import Path

# 序列化任意 Python 对象
data = {"numbers": [1, 2, 3], "func": lambda x: x**2}

pickled = pickle.dumps(data)
print(f"Pickle 大小: {len(pickled)} bytes")

# 写入文件
Path("data.pkl").write_bytes(pickled)

# 反序列化
loaded = pickle.loads(Path("data.pkl").read_bytes())
print(loaded["numbers"])

# 注意:Pickle 不安全!不要反序列化不受信任的数据

8. MessagePack:二进制 JSON

# pip install msgpack
import msgpack

data = {"name": "Alice", "age": 25, "scores": [95, 88, 92]}

# 序列化(比 JSON 更快、更小)
packed = msgpack.packb(data)
print(f"MessagePack 大小: {len(packed)} bytes")

# 反序列化
loaded = msgpack.unpackb(packed)

9. Parquet:列式存储

# pip install pyarrow
import pandas as pd

df = pd.DataFrame({
    "name": ["Alice", "Bob", "Charlie"],
    "age": [25, 30, 35],
    "salary": [5000.0, 6000.0, 7000.0],
})

# 写入 Parquet(列式存储,压缩率高)
df.to_parquet("data.parquet", engine="pyarrow", compression="snappy")

# 读取(只读需要的列,非常快)
df = pd.read_parquet("data.parquet", columns=["name", "age"])

10. 大数据分块读取

from pathlib import Path

def process_large_file(filepath, process_func, chunk_size=8192):
    """分块处理大文件"""
    with open(filepath, "rb") as f:
        while chunk := f.read(chunk_size):
            process_func(chunk)

# 逐行处理超大日志
def process_logs(filepath):
    with open(filepath, "r", encoding="utf-8") as f:
        for line in f:
            yield line.strip()

# 使用
for line in process_logs("huge.log"):
    if "ERROR" in line:
        print(line)

11. 编码处理

# 自动检测编码
# pip install chardet
import chardet

raw = Path("unknown.txt").read_bytes()
result = chardet.detect(raw)
print(f"编码: {result['encoding']}, 置信度: {result['confidence']}")

# 按检测到的编码解码
text = raw.decode(result["encoding"])

# BOM 处理
# utf-8-sig 自动处理 UTF-8 BOM
Path("with_bom.txt").write_text("hello", encoding="utf-8-sig")

延伸阅读


文件 IO 和序列化是数据工程的基石。选对格式(文本 vs 二进制 vs 列式)、处理好编码、掌握内存友好的读取方式,是高效处理数据的前提。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「python」更多文章

  1. Python 高级异步编程:Trio 结构化并发与 AnyIO 兼容层
  2. Python 数据工程与 ETL 管道实战
  3. Python 元编程与动态特性深度解析