飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

JSON 与 CSV

程序经常需要把内存中的结构化数据持久化到磁盘,或与其他系统交换数据。JSON 和 CSV 是 Python 标准库直接支持的两种纯文本格式:JSON 擅长表达嵌套的层级结构,是跨语言数据交换的事实标准;CSV 则是扁平表格的最简表示,能被 Excel 直接打开。二者都涉及编码、换行符和类型转换等细节,处理不当会导致数据损坏或读取失败。

JSON 模块

json 模块提供两组核心函数:操作字符串的 dumps / loads,以及直接对接文件对象的 dump / load。名称末尾的 s 代表 string,是区分二者的关键记忆点。

字符串层面的序列化与反序列化

json.dumps(obj) 将 Python 对象转为 JSON 格式的字符串,这一过程称为序列化(serializing)。json.loads(s) 则反向执行反序列化(deserializing)。

import json

x = [1, 'simple', 'list']
print(json.dumps(x))       # '[1, "simple", "list"]'

s = '{"name": "Alice", "age": 30, "active": true}'
print(json.loads(s))       # {'name': 'Alice', 'age': 30, 'active': True}

Python 类型与 JSON 类型的映射关系如下:

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

注意:JSON 的键必须是字符串,因此 Python 字典的整数键会被强制转为字符串;元组会被转为数组(反序列化时变回列表,信息丢失)。

data = {(1, 2): 'value'}   # 元组作键
print(json.dumps(data))    # '{"[1, 2]": "value"}'  ← 键被强制字符串化

文件层面的序列化与反序列化

dump(obj, fp) 和 load(fp) 直接读写文件对象,省去手动中转字符串的步骤。JSON 规范要求文件使用 UTF-8 编码,因此打开文件时必须显式指定 encoding='utf-8'。

employee = {
    'company': 'Example Inc.',
    'founded': 2018,
    'team': [
        {'name': 'Alice', 'role': 'backend'},
        {'name': 'Bob', 'role': 'frontend'}
    ],
    'active': True
}

# 写入
with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(employee, f)

# 读取
with open('data.json', 'r', encoding='utf-8') as f:
    obj = json.load(f)
print(obj['team'][0]['name'])   # Alice

格式化输出

dumps 和 dump 提供若干参数控制输出格式:

  • indent:缩进空格数,美化输出。设为 None(默认)则压缩为一行。
  • ensure_ascii=False:允许非 ASCII 字符(如中文)原样输出,而不是转义为 \uXXXX。
  • sort_keys=True:按键名排序,便于版本控制 diff。
with open('pretty.json', 'w', encoding='utf-8') as f:
    json.dump(employee, f, ensure_ascii=False, indent=2, sort_keys=True)

自定义序列化

默认情况下,datetime 对象、自定义类实例等无法直接序列化,会抛出 TypeError。可通过 default 参数传入一个钩子函数,将不支持的对象转为可序列化类型:

from datetime import datetime

def custom_serializer(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    raise TypeError(f'Object of type {type(obj).__name__} is not JSON serializable')

now = {'created': datetime.now()}
print(json.dumps(now, default=custom_serializer))
# {"created": "2024-06-01T14:30:00.000000"}

反序列化时,可用 object_hook 将字典转为自定义对象,实现类型还原。

性能与安全边界

JSON 是纯文本格式,人类可读且语言无关,但不支持 Python 特有的对象类型(如集合、复数、任意类实例)。若需要在 Python 进程间保存完整对象图,标准库提供 pickle 模块,但它使用私有协议,不能与其他语言交互,且默认不安全——解序列化恶意构造的 pickle 数据可能执行任意代码。因此,永远不要对不信任来源的数据使用 pickle.load()。

CSV 模块

CSV(Comma-Separated Values)用换行分隔行、逗号分隔列,是最简单的表格格式。Python 的 csv 模块处理读写,并自动应对引号、转义和换行符等边界情况。

写入 CSV

csv.writer(fileobj) 返回一个 writer 对象,其 writerow(row) 方法写入单行,writerows(rows) 写入多行。row 是可迭代对象(通常为列表),每个元素转为字符串后输出。

import csv

staff = [
    ['姓名', '部门', '年龄'],
    ['Alice', '技术部', 28],
    ['Bob', '产品部', 26]
]

with open('staff.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerows(staff)

newline='' 至关重要。CSV 模块自己负责处理换行符,若使用平台默认换行(Windows 下为 \r\n),csv.writer 会在每行末尾再追加一个 \r,导致文件出现空行。传入 newline='' 将换行控制权完全交给 csv 模块,可生成符合 RFC 4180 标准的文件。

csv.writer 还支持 delimiter(分隔符,默认逗号)、quotechar(引号字符,默认 ")、quoting(引号策略)等参数。当字段包含逗号或换行时,模块会自动加引号包裹:

with open('complex.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['备注', '包含,逗号\n和换行'])
# 输出:备注,"包含,逗号
# 和换行"

读取 CSV

csv.reader(fileobj) 返回一个迭代器,每次产生一行(字符串列表)。

with open('staff.csv', 'r', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
# ['姓名', '部门', '年龄']
# ['Alice', '技术部', '28']

注意:所有读出的字段都是字符串,数字需要手动 int() / float() 转换。若第一行是表头,可用 csv.DictReader 将每行映射为字典,键取自表头:

with open('staff.csv', 'r', newline='', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(f"{row['姓名']} — {row['部门']}")

DictReader 的字段名可通过 fieldnames 参数显式指定,或让模块自动从第一行推断。若表头缺失,建议手动传入 fieldnames 避免把数据行误当表头。

with open('no_header.csv', 'r', newline='', encoding='utf-8') as f:
    reader = csv.DictReader(f, fieldnames=['name', 'dept', 'age'])
    for row in reader:
        print(row)

字典方式写入

csv.DictWriter 与 DictReader 对应,接受字典序列并按固定字段顺序输出:

with open('out.csv', 'w', newline='', encoding='utf-8') as f:
    fieldnames = ['name', 'dept', 'age']
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({'name': 'Alice', 'dept': '技术部', 'age': 28})
    writer.writerows([
        {'name': 'Bob', 'dept': '产品部', 'age': 26},
        {'name': 'Carol', 'dept': '设计部', 'age': 25}
    ])

若字典缺少某个字段,DictWriter 默认写入空字符串;若多出未声明的字段,默认忽略。可通过 restval 和 extrasaction 参数调整这一行为。

编码与换行符处理

JSON 和 CSV 都是纯文本格式,编码问题尤为突出。

  • JSON:RFC 8259 明确规定 JSON 文本必须是 UTF-8(无 BOM)。读写时始终使用 encoding='utf-8'。json.dumps 的 ensure_ascii=False 让中文直接写入,避免 \u4e2d\u6587 这类转义,既减小体积又提高可读性。
  • CSV:RFC 4180 未规定编码,但实际使用中 UTF-8 已成为默认选择。Excel 打开 UTF-8 CSV 时,若含 BOM(utf-8-sig)可自动识别中文;否则可能需要手动选择编码。若目标用户主要使用 Excel,写入时可考虑 encoding='utf-8-sig',它在文件头添加 BOM,帮助 Excel 正确识别。

换行符方面,JSON 的 dump 使用 \n 作为缩进换行,与平台无关。CSV 则必须将 newline='' 传给 open(),否则 Windows 平台会产生 \r\r\n 的畸形换行。

总结

json 模块的 dumps/loads 操作字符串,dump/load 操作文件,配合 ensure_ascii=False 和 indent 可生成人类可读的 UTF-8 文本。自定义对象通过 default 钩子序列化。csv 模块的 reader/writer 处理列表,DictReader/DictWriter 处理字典,读写时都必须指定 newline='' 防止空行。两种格式都建议显式声明 encoding='utf-8',JSON 用于层级数据交换,CSV 用于扁平表格互通,各安其位。

上一页
字符串格式化