| Day 12 | CSV 与 JSON:序列化与数据存取 |

1 数据持久化 —— 让程序不再失忆

是什么?为什么学?

持久化(persistence)就是把程序运行期间存在内存里的数据,保存到硬盘上,让程序关闭之后数据依然存在。内存的致命缺点是断电即失——程序一关,所有变量、列表、字典全部清零。学会它,你的程序才真正开始「积累数据」,这是从「玩具程序」走向「实用工具」的分水岭。

底层原理

程序运行时数据存在内存(RAM)里,访问极快但断电消失;硬盘上的文件可以永久保存。持久化的本质是:把内存中的数据结构,翻译成一种能写成文件、又能从文件读回来的格式——翻译过去叫「序列化」,翻译回来叫「反序列化」。今天学的 CSV 和 JSON 就是两种「翻译格式」,csvjson 模块就是「翻译官」。

术语小词典:序列化(serialization)就是把内存里的对象「翻译成能存进文件的文本」;反序列化(deserialization)就是反过来「从文本还原成对象」。

生活类比

持久化像写日记 vs 记在脑子里:不写日记,睡一觉(程序关闭)就全忘了;写进日记本(文件),明天翻开还记得。CSV 和 JSON 是两种「日记本」:CSV 是表格,JSON 是带层级的卡片。

注意事项总结

1 没有持久化的程序——数据用完就没了

students = ["张三", "李四"]
print("程序运行中:", students)

运行结果:

程序运行中: ['张三', '李四']

列表存在内存里,程序一结束内存被回收,数据归零——下次运行又要从头输入。

2 有持久化的程序——数据存进文件,下次还能读回来

with open("memo.txt", "w", encoding="utf-8") as f:
    f.write("张三\n李四\n")

with open("memo.txt", "r", encoding="utf-8") as f:
    restored = f.read()
print(restored)

运行结果:

张三
李四

第一段 "w" 模式把数据写进文件,第二段 "r" 模式读回变量。程序关掉再打开,只要文件还在,数据就还在——这就是「存-读」闭环。

易错点

  • 错误写法:只写不读:写完文件后从不验证 → 问题:存的时候报错了你都不知道 → 正确写法:写完文件后马上读一遍验证(闭环测试)。
  • 错误写法:用 "w" 模式反复写同一个文件 → 问题:每次打开都会清空原内容,旧数据被覆盖 → 正确写法:要保留旧数据改用 "a" 追加,或换文件名。
  • 错误写法:以为数据存进变量就「保存」了 → 问题:变量在内存里,程序一关就没了 → 正确写法:保存 = 写到文件(open + 写操作)。

记忆口诀:内存断电就清零,写进文件才保命;存读闭环要验证,写完马上读一遍。

2 CSV —— 给表格数据准备的通用格式

是什么?为什么学?

CSV(Comma-Separated Values,逗号分隔值):用逗号把「一行的各个单元格」分隔开的纯文本表格格式,第一行常是表头(列名),后面每行是一条记录。它的优势是极简、通用:Excel、WPS、任何编程语言都能直接处理。成绩表、订单表、日志导出都绕不开它。

底层原理

CSV 文件本质就是一个纯文本文件:每行是一条记录,行内单元格用逗号隔开。难点在「边界情况」:如果单元格内容本身含逗号(如备注”喜欢,篮球”),直接按逗号切分就会切错。所以 csv 模块写入时自动给特殊内容加引号,读取时自动还原——你不用手动处理这些细节。

生活类比

CSV 像超市的购物小票:一行一行打,每项用逗号隔开,机器(Excel)拿到小票就能识别每一列。就算某项里含有逗号,打印机会用引号把它「包起来」防止认错——csv 模块就是这台自动打印机。

注意事项总结

1 csv.writer 写文件 + csv.reader 读文件(最基础的一对)

import csv
with open("students.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["姓名", "年龄"])      # 表头:一列一个名字
    writer.writerow(["张三", 18])
    writer.writerow(["李四", 19])
with open("students.csv", "r", encoding="utf-8") as f:
    for row in csv.reader(f):
        print(row)

运行结果:

['姓名', '年龄']
['张三', '18']
['李四', '19']

csv.writer(f) 创建写入器,writerow([...]) 每调用一次写一行;csv.reader(f)for 循环逐行拿到列表newline="" 是 Windows 上防止多余空行的固定搭配。

2 单元格里含逗号怎么办——csv 模块自动用引号包起来

import csv
with open("names.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["姓名", "备注"])
    writer.writerow(["小明", "喜欢,篮球"])   # 备注里有一个英文逗号!
with open("names.csv", "r", encoding="utf-8") as f:
    for row in csv.reader(f):
        print(row)

运行结果:

['姓名', '备注']
['小明', '喜欢,篮球']

csv.writer 检测到内容含逗号,自动加引号(打开文件能看到 "喜欢,篮球");读取时自动去掉引号。自己用 split(",") 切会切成三个单元格。

3 DictWriter / DictReader——用「列名」读写,不再记下标

import csv

with open("scores.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["姓名", "语文", "数学"])
    writer.writeheader()                     # 用列名列表自动写表头
    writer.writerow({"姓名": "张三", "语文": 90, "数学": 85})

with open("scores.csv", "r", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        print(row["姓名"], "的语文成绩:", row["语文"])

运行结果:

张三 的语文成绩: 90

DictWriter 写入时用字典,列名由 fieldnames 统一声明,writeheader() 自动写表头;DictReader 读取时每行是字典,用列名 row["姓名"] 取值。读出来的值全是字符串

易错点

  • 错误写法:写 CSV 时漏掉 newline="" → 问题:Windows 上每行之间多一个空行 → 正确写法:写 CSV 固定 open(file, "w", newline="", encoding="utf-8")
  • 错误写法:直接对文件对象用 split(",") 切行 → 问题:单元格内含逗号时被错误切分 → 正确写法:一律用 csv.reader / csv.writer 处理。
  • 错误写法:读取后直接 row[1] + 1 做运算 → 问题:读出来是字符串 '18',字符串加数字报错 → 正确写法:先转换:int(row[1])

记忆口诀:写带 newline,读用 for 行;逗号含特殊,模块自动包;列名好记用 Dict。

3 JSON —— 嵌套数据的通用格式

是什么?为什么学?

JSON(JavaScript Object Notation,JavaScript 对象表示法):用「花括号 + 冒号」描述嵌套数据的文本格式,语法和 Python 的字典 / 列表几乎一样。它是现代程序之间交换数据的事实标准:网页和服务器、配置文件、爬虫数据全都用它。学 JSON,你的程序就能「说全世界的通用语言」,还能把任意嵌套结构完整地存进文件再读回来。

底层原理

JSON 只有 6 种数据类型:对象 {}(对应 Python 字典)、数组 [](对应列表)、字符串、数字、布尔值(true / false)、空值 nulljson 模块负责「翻译」:dumps / dump 把 Python 对象翻译成 JSON 文本,loads / load 翻译回来。为什么不能直接把对象写进文件? 文件只能存文本,字典、列表是内存里的复杂结构,必须先「翻译成文本」才能落盘。注意:JSON 的字符串和键名必须用双引号——这是新手最容易踩的坑。

生活类比

JSON 像快递面单:寄件信息、收件信息、物品清单都按固定格式填在一张单子上(嵌套:面单里又分「寄件信息」「收件信息」两块)。快递公司(另一个程序)拿到面单,按格式就能看懂全部信息——JSON 就是程序之间的「通用面单」。

注意事项总结

1 dumps / loads——字符串与对象的互转

import json

student = {"name": "张三", "age": 18}
text = json.dumps(student, ensure_ascii=False)
print(text)
print(type(text))
back = json.loads(text)
print(back["name"], back["age"] + 2)

运行结果:

{"name": "张三", "age": 18}
<class 'str'>
张三 20

json.dumps 把字典翻译成 JSON 字符串(键名变双引号);ensure_ascii=False 让中文原样显示;json.loads 把字符串翻译回字典,back["age"] + 2 证明它能像普通字典一样运算。

2 dump / load——直接写文件和读文件(持久化标准姿势)

import json

students = [{"name": "张三", "score": 90}, {"name": "李四", "score": 78}]

with open("students.json", "w", encoding="utf-8") as f:
    json.dump(students, f, ensure_ascii=False, indent=2)

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

print(type(loaded))
print(loaded[1]["name"])

运行结果:

<class 'list'>
李四

json.dump(students, f, ...) 把整个列表写进文件(不用加 newline="");json.load(f) 从文件读回,类型仍是 list。存进去什么样,读出来还是什么样——这是 JSON 持久化最省心的地方。

3 嵌套数据——JSON 的真正优势

import json

data = {
    "班级": "Python 班",
    "学生": [
        {"name": "张三", "scores": [90, 85]},
        {"name": "李四", "scores": [78, 92]},
    ],
}
text = json.dumps(data, ensure_ascii=False)   # 不带 indent,输出紧凑版
print(text)
back = json.loads(text)
print(back["学生"][0]["scores"][1])

运行结果:

{"班级": "Python 班", "学生": [{"name": "张三", "scores": [90, 85]}, {"name": "李四", "scores": [78, 92]}]}
85

这是「字典套列表、列表套字典」的三层嵌套,CSV 表达不了,JSON 却原样保留。想要整齐的层级缩进,加上 indent=2 即可。层级越深,JSON 的优势越大。

易错点

  • 错误写法:写 JSON 时忘了 ensure_ascii=False → 问题:中文全变成 \u5f20 转义码,人没法读 → 正确写法:json.dump(..., ensure_ascii=False)
  • 错误写法:手写 JSON 用了单引号({'name': '张三'}) → 问题:json.loadsJSONDecodeError,JSON 标准要求双引号 → 正确写法:手写 JSON 一律用双引号。
  • 错误写法:混淆 dump / dumps → 问题:dump 要文件对象,dumps 要字符串,传错报错 → 正确写法:带 s(string)的处理字符串,不带 s 的处理文件。

记忆口诀:dumps 出字符串,loads 吃字符串;dump 进文件,load 出文件;中文加 False,缩进写 indent。

4 CSV vs JSON —— 怎么选?怎么互相转换?

是什么?为什么学?

学了两种格式,最实际的问题是:这份数据到底该用 CSV 还是 JSON? 选错了,要么嵌套结构塞不进 CSV,要么简单表格白白增加复杂度。这一节先学会「按数据结构选格式」,再学会「CSV 读进来 → JSON 存出去」的转换套路——「表格数据进系统,JSON 存内部」是真实项目里极常见的需求。

底层原理

两者的本质区别在「维度」:CSV 是二维平面——只有「行 × 列」,一格只能放一个值;JSON 是任意深度——通过对象和数组无限嵌套。选择规则很朴素:数据是规整表格 → CSV;数据有嵌套 → JSON。转换也不难:CSV 读出来是「行列表 / 字典列表」,JSON 存的也是「列表套字典」,结构一致,直接「读 A 写 B」就是转换。

生活类比

CSV 像Excel 表格:横平竖直,适合整齐数据;JSON 像文件夹套文件夹:张三是一个文件夹,里面再放「语文、数学」两个子文件夹,层级分明。

注意事项总结

1 判断用哪种格式——先看结构再选

rows = [{"name": "张三", "age": 18}, {"name": "李四", "age": 19}]
nested = {"学生": [{"name": "张三", "scores": {"语文": 90}}, {"name": "李四", "scores": {"语文": 78}}]}
print("rows 是规整表格 → 用 CSV;nested 有嵌套层级 → 用 JSON")

rows 是平铺的「姓名 + 年龄」,两列就能放下——CSV;nested 里学生套着 scores 字典,是两层结构——JSON。判断口诀:能否用「行 × 列」摆平?能就 CSV,不能就 JSON

2 CSV → JSON 转换(表格数据转嵌套存储)

import csv
import json

rows = [{"name": "张三", "age": 18}, {"name": "李四", "age": 19}]

with open("data.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["name", "age"])
    writer.writeheader()
    writer.writerows(rows)

with open("data.json", "w", encoding="utf-8") as f:
    json.dump(rows, f, ensure_ascii=False, indent=2)

转换的本质就是「同一份 Python 数据,换一种格式落盘」——先读进内存,再按目标格式写出去。

易错点

  • 错误写法:把嵌套数据硬塞进 CSV(writerow([scores字典])) → 问题:字典被 str 成一段难看的文本,读回来无法还原 → 正确写法:嵌套结构用 JSON,只有平铺表格才用 CSV。
  • 错误写法:转换时直接 csv.reader 读 → 写 JSON → 问题:读出来的单元格全是字符串,数字变文本 → 正确写法:转换前把需要的列 int() / float() 转回数字。
  • 错误写法:忘了 CSV 表头对齐(列数不一致) → 问题:Excel 打开时数据错列,无法解析 → 正确写法:用 DictWriter + fieldnames 固定列名和顺序。

记忆口诀:平铺表格用 CSV,嵌套结构用 JSON;转换先读进内存,再换格式写出去。

5 动手实践:成绩表导入导出工具

做一个「成绩表工具」:能录入学生成绩、展示全部成绩、导出成 CSV(给 Excel 用)、保存成 JSON(给程序用)。

# score_tool.py —— 成绩表导入导出工具
import csv
import json

students = []   # 内存数据:每个学生一个字典

def add_student():
    name = input("姓名:")
    chinese = int(input("语文:"))
    math = int(input("数学:"))
    students.append({"name": name, "语文": chinese, "数学": math})
    print(f"已添加 {name}")

def show_all():
    if not students:
        print("暂无数据")
        return
    print(f"{'姓名':<6}{'语文':<6}{'数学':<6}")
    for s in students:
        print(f"{s['name']:<6}{s['语文']:<6}{s['数学']:<6}")

def export_csv():
    with open("scores_export.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(["姓名", "语文", "数学"])
        for s in students:
            writer.writerow([s["name"], s["语文"], s["数学"]])
    print("已导出 scores_export.csv")

def export_json():
    with open("scores_export.json", "w", encoding="utf-8") as f:
        json.dump(students, f, ensure_ascii=False, indent=2)
    print("已导出 scores_export.json")

def main():
    while True:
        print("\n1.添加学生  2.查看全部  3.导出CSV  4.导出JSON  5.退出")
        choice = input("请选择:")
        if choice == "1":
            add_student()
        elif choice == "2":
            show_all()
        elif choice == "3":
            export_csv()
        elif choice == "4":
            export_json()
        elif choice == "5":
            break
        else:
            print("无效选择")

if __name__ == "__main__":
    main()

运行效果:

1.添加学生  2.查看全部  3.导出CSV  4.导出JSON  5.退出
请选择:1
姓名:张三
语文:90
数学:85
已添加 张三
请选择:2
姓名    语文    数学
张三    90    85

导出后用 Excel 打开 CSV、用记事本打开 JSON,都能看到刚才的数据。

升级挑战:加一个「从 CSV 导入」功能,把 scores.csv 里的数据读回 students 列表(记得把字符串转成数字)。

今日总结

持久化是什么:把内存数据写进文件、程序重启后读回来(「存-读」闭环) CSV 适合规整表格:csv.writer / reader 读写,DictWriter / DictReader 用列名读写,写 CSV 记得 newline="" CSV 读出来的单元格全是字符串,要计算必须先转数字 JSON 适合嵌套结构:dumps / loads 管字符串,dump / load 管文件 写 JSON 记得 ensure_ascii=False(中文原样)和 indent=2(美化缩进) CSV ↔ JSON 转换:先读进内存(同一份 Python 数据),再按目标格式写出去 成绩表工具可以录入、查看并导出 CSV 和 JSON

报错原因修复
json.decoder.JSONDecodeErrorJSON 文件格式坏了,或读了一个根本不是 JSON 的文件检查文件内容,或用在线校验工具检查格式
KeyError: 'name'字典里没有这个键(如 CSV 表头对不上)打印数据先看结构,确认键名一致
csv.Error: iterator should return strings试图把数字直接交给 writerow 之外的非字符串数据统一用字符串或确保数据是字符串
写 CSV 后 Excel 打开每行中间有空行打开文件时少了 newline=""写 CSV 时加上 newline=""
中文读出来变 \uXXXX写 JSON 时没加 ensure_ascii=Falsejson.dump(..., ensure_ascii=False)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇