证件、账号、联系方式——把这一路学的所有东西,装进一个真正属于你的程序
一、你有多少”重要信息”散落在各处?
先别写代码,盘点一下:
- 身份证号、护照号、驾照号——平时用不到,用到时找不到
- 各种账号:邮箱、视频网站、购物网站、健身房卡号……
- 家人的身份证号(订机票要用)
- 银行卡号、社保号、保单号
- 重要日期:签证过期、证件到期、保修截止
这些信息有个共同点:不用时想不起,要用时找不到。翻聊天记录、翻抽屉、问家人——每次都要折腾半天。
手机备忘录?可以,但查找麻烦、没法分类、明文摆着还不安全。
今天,我们自己做一个个人档案管理器。它是阶段2的收官之作——这一篇会用上你学过的所有知识点:字典列表、增删改查、文件读写、错误处理、函数、搜索、统计。没有一个新知识,但组合起来,就是一个真正能用的软件。
这也是编程最有魅力的时刻:你不再是在”练习知识点”,而是在”造工具”。
二、功能设计
一个称职的档案管理器需要:
- ➕ 添加一条档案(比如”身份证”)
- 📋 查看所有档案
- 🔍 搜索档案(输”身份”能找到”身份证”)
- ✏️ 修改档案(信息变了)
- 🗑️ 删除档案(不要了)
- 📁 分类管理(证件/账号/金融/其他)
- 💾 保存到文件,下次打开还在
- 🔐 加密存储(改进一)
- 📊 导出报表(改进三)
一眼看过去全是熟面孔——第十二篇价格工具、第十七篇成绩管理器、第十八篇待办事项工具,都是这个”增删改查+文件”的框架。今天在这个框架上,加两样新花样:加密和报表。
三、数据结构:一条档案长什么样
{
"名称": "身份证",
"类别": "证件",
"号码": "110101199001011234",
"备注": "2030年到期"
}
一个字典一条档案,所有档案装一个列表。类别用”证件/账号/金融/其他”四种。
档案列表 = [] # 列表装字典——你的看家本领了
四、先跟AI聊
你的提示词:
用Python写一个个人档案管理器(命令行,中文界面)。管理个人信息如证件号、账号等。每条档案包含:名称、类别(证件/账号/金融/其他)、号码、备注。功能:添加、查看全部、按名称搜索、修改、删除、按分类查看,数据保存到JSON文件、启动自动加载。用列表存档案、每条是字典,各功能封装成函数,主循环显示菜单。代码加详细中文注释。
五、主体代码
import json
文件名 = "我的档案.json"
档案列表 = [] # 全部档案:列表装字典
类别表 = ["证件", "账号", "金融", "其他"] # 固定的四个类别
# ============ 文件操作 ============
def 加载档案():
"""启动时从文件读取档案"""
global 档案列表
try:
with open(文件名, "r", encoding="utf-8") as f:
档案列表 = json.load(f)
print(f"📂 已加载 {len(档案列表)} 条档案")
except FileNotFoundError:
print("📭 没有找到档案文件,从空档案开始")
except json.JSONDecodeError:
print("⚠️ 文件损坏,从空档案开始(原文件已备份为.bak)")
import os
os.rename(文件名, 文件名 + ".bak") # 坏文件改名留证,不直接删
def 保存档案():
"""把全部档案写入文件"""
with open(文件名, "w", encoding="utf-8") as f:
json.dump(档案列表, f, ensure_ascii=False, indent=2)
print(f"💾 已保存 {len(档案列表)} 条档案")
# ============ 添加 ============
def 选类别():
"""显示类别菜单,返回用户选的类别名"""
for i, 名称 in enumerate(类别表, 1):
print(f" {i}. {名称}")
选择 = input("选择类别(输入数字):").strip()
try:
return 类别表[int(选择) - 1]
except (ValueError, IndexError):
print("无效选择,归入'其他'")
return "其他"
def 添加档案():
"""添加一条新档案"""
名称 = input("档案名称(如:身份证):").strip()
if not 名称:
print("❌ 名称不能为空")
return
# 查重:名称不能重复
for 条 in 档案列表:
if 条["名称"] == 名称:
print(f"❌ 已经有叫"{名称}"的档案了,换个名字或去修改")
return
类别 = 选类别()
号码 = input("号码/内容:").strip()
if not 号码:
print("❌ 号码不能为空")
return
备注 = input("备注(可留空):").strip()
新档案 = {"名称": 名称, "类别": 类别, "号码": 号码, "备注": 备注}
档案列表.append(新档案)
print(f"✅ 已添加:{名称}")
# ============ 查看 ============
def 显示档案表(若干条):
"""把一批档案打印成表格(供多个功能复用)"""
if not 若干条:
print("📭 (空)")
return
print(f"n{'名称':<12}{'类别':<8}{'号码':<22}{'备注'}")
print("-" * 56)
for 条 in 若干条:
print(f"{条['名称']:<12}{条['类别']:<8}{条['号码']:<22}{条['备注']}")
print(f"共 {len(若干条)} 条")
def 查看全部():
"""显示所有档案"""
显示档案表(档案列表)
def 按分类查看():
"""只看某一类的档案"""
类别 = 选类别()
筛出的 = [条 for 条 in 档案列表 if 条["类别"] == 类别]
print(f"n【{类别}】类档案:")
显示档案表(筛出的)
# ============ 搜索 ============
def 搜索档案():
"""按名称或备注模糊搜索"""
关键字 = input("输入关键字:").strip()
if not 关键字:
return
结果 = [条 for 条 in 档案列表
if 关键字 in 条["名称"] or 关键字 in 条["备注"]]
print(f"n搜索"{关键字}"的结果:")
显示档案表(结果)
# ============ 修改 ============
def 按名称找(名称):
"""辅助函数:按名称找档案,找到返回字典,找不到返回None"""
for 条 in 档案列表:
if 条["名称"] == 名称:
return 条
return None
def 修改档案():
"""修改一条档案的信息"""
名称 = input("要修改哪条档案的名称:").strip()
条 = 按名称找(名称)
if 条 is None:
print(f"❌ 找不到"{名称}"")
return
print(f"当前信息:{条}")
print("直接回车表示该项不改n")
新号码 = input(f"新号码(现:{条['号码']}):").strip()
if 新号码:
条["号码"] = 新号码
新备注 = input(f"新备注(现:{条['备注']}):").strip()
if 新备注:
条["备注"] = 新备注
print(f"✅ 已更新:{名称}")
# ============ 删除 ============
def 删除档案():
"""删除一条档案(需二次确认)"""
名称 = input("要删除哪条档案:").strip()
条 = 按名称找(名称)
if 条 is None:
print(f"❌ 找不到"{名称}"")
return
print(f"将要删除:{条}")
确认 = input("⚠️ 确认删除?此操作不可恢复!(输入yes):").strip()
if 确认 == "yes":
档案列表.remove(条) # 按内容删除列表元素
print(f"🗑️ 已删除:{名称}")
else:
print("取消删除")
# ============ 主菜单 ============
def 主菜单():
加载档案()
菜单项 = {
"1": ("添加档案", 添加档案),
"2": ("查看全部", 查看全部),
"3": ("按分类查看", 按分类查看),
"4": ("搜索档案", 搜索档案),
"5": ("修改档案", 修改档案),
"6": ("删除档案", 删除档案),
"7": ("保存", 保存档案),
"0": ("退出", None)
}
while True:
print("n" + "=" * 28)
print(" 🗂️ 个人档案管理器")
print("=" * 28)
for 编号, (名称, _) in 菜单项.items():
print(f" {编号}. {名称}")
选择 = input("请选择:").strip()
if 选择 == "0":
保存档案()
print("👋 再见!档案已保存。")
break
elif 选择 in 菜单项:
菜单项[选择][1]() # 取出函数并调用
else:
print("❌ 无效选择")
主菜单()
六、这段代码里的”毕业知识点”
大部分你都见过,这里挑三个设计点讲:
6.1 字典装函数:菜单的高级写法
菜单项 = {
"1": ("添加档案", 添加档案),
...
}
elif 选择 in 菜单项:
菜单项[选择][1]() # 取出函数并调用
以前的菜单是一长串if 选择 == "1": 添加档案()。现在把函数本身存进字典,菜单项["1"][1]取出的就是添加档案这个函数,加个()就调用它。
函数可以像数字、文字一样传来传去——第七篇埋下的伏笔,今天用上了。这种写法的好处:菜单显示和功能执行用同一份数据,加功能只需往字典加一行。
6.2 辅助函数:按名称找
def 按名称找(名称):
for 条 in 档案列表:
if 条["名称"] == 名称:
return 条
return None
修改和删除都要”先找到再操作”,那就把”找”抽成一个函数,两处调用。一个功能只写一遍——第七篇函数的核心价值。注意两个return:找到返回那条字典,找不到返回None。调用方用is None判断找没找到。
6.3 删除要确认
确认 = input("⚠️ 确认删除?此操作不可恢复!(输入yes):").strip()
if 确认 == "yes":
要求用户明确输入”yes”而不是按y——危险操作要提高确认门槛。删错档案可能损失重要信息,宁可让用户多敲三个字母。真实软件里”请输入DELETE确认删除”就是这个思路。
6.4 文件损坏的处理
except json.JSONDecodeError:
import os
os.rename(文件名, 文件名 + ".bak")
文件坏了不是简单地”从空开始”——那样原数据就没了。先把它改名备份成.bak,再从空开始。万一只是小问题,还能手动修复备份文件。永远不要静默销毁用户数据。
七、运行试试
python 档案管理器.py
📂 已加载 0 条档案
添加几条:身份证、常用邮箱、健身卡、医保卡。然后试试搜索(输”卡”能搜到健身卡和医保卡)、按分类查看、修改、删除。
主体完成。现在开始加三个真正的亮点功能。
八、第一个改进:简单加密 — 文件里不存明文
打开我的档案.json一看:身份证号明晃晃躺在里面。这不行——文件被别人看到就全泄露了。
专业的加密很复杂,但我们可以做一个够用的简单版:把每个字符的编码”挪一挪”(凯撒密码的思路)。
def 加密文本(文本, 密钥=7):
"""把文字打乱:每个字符的编码加上密钥"""
结果 = ""
for 字 in 文本:
结果 += chr(ord(字) + 密钥) # 每个字"往后挪7位"
return 结果
def 解密文本(文本, 密钥=7):
"""把打乱的文字还原:编码减去密钥"""
结果 = ""
for 字 in 文本:
结果 += chr(ord(字) - 密钥)
return 结果
ord(字):字符→数字。ord("A")是65chr(数字):数字→字符。chr(65)是”A”- 加密就是每个字符+7,解密就是每个字符-7。”abc”加密成”hij”,解密回来还是”abc”
然后改造存取过程——存的时候加密,读的时候解密:
def 保存档案():
"""保存前把每条档案的号码和备注加密"""
待存 = []
for 条 in 档案列表:
待存.append({
"名称": 条["名称"], # 名称和类别不加密(方便人眼看文件)
"类别": 条["类别"],
"号码": 加密文本(条["号码"]), # 敏感字段加密
"备注": 加密文本(条["备注"])
})
with open(文件名, "w", encoding="utf-8") as f:
json.dump(待存, f, ensure_ascii=False, indent=2)
print(f"💾 已加密保存 {len(待存)} 条档案")
def 加载档案():
global 档案列表
try:
with open(文件名, "r", encoding="utf-8") as f:
原始 = json.load(f)
# 读进来后解密还原
档案列表 = []
for 条 in 原始:
档案列表.append({
"名称": 条["名称"],
"类别": 条["类别"],
"号码": 解密文本(条["号码"]),
"备注": 解密文本(条["备注"])
})
print(f"📂 已加载 {len(档案列表)} 条档案")
except FileNotFoundError:
print("📭 没有找到档案文件,从空档案开始")
现在文件里长这样:
{"名称": "身份证", "类别": "证件", "号码": "8181818qyqy...", "备注": "..."}
号码变成了一串乱码,别人打开文件也看不懂。
必须说清楚:这不是真正的密码学加密!知道原理的人轻松能解。但对”防室友瞟一眼””防文件误传”这种日常场景,够用了。真实的密码管理器用的是AES这类专业加密——那是以后学了加密库才能碰的东西。今天重点是理解”存时变换、取时还原“这个模式。
九、第二个改进:分类统计 — 你的信息都藏在哪
给管理器加一个”体检”功能:看看档案的分布情况。
def 统计报告():
"""统计各类别档案数量"""
if not 档案列表:
print("📭 没有档案")
return
# 分类计数:字典累加
计数 = {}
for 条 in 档案列表:
类别 = 条["类别"]
计数[类别] = 计数.get(类别, 0) + 1
print("n📊 档案分布")
print("-" * 30)
for 类别 in 类别表: # 按固定类别顺序显示
数量 = 计数.get(类别, 0)
条形 = "█" * 数量 # 字符串乘法画条形图
print(f" {类别:2}条 {条形}")
print("-" * 30)
print(f" 总计 {len(档案列表)} 条")
# 附加信息:有没有快到期的(备注里带"到期"的)
到期的 = [条["名称"] for 条 in 档案列表 if "到期" in 条["备注"]]
if 到期的:
print(f"n⏰ 备注里提到"到期"的档案:{'、'.join(到期的)}")
print(" 记得检查这些证件的有效期!")
效果:
📊 档案分布
------------------------------
证件 3条 ███
账号 2条 ██
金融 1条 █
其他 0条
------------------------------
总计 6 条
⏰ 备注里提到"到期"的档案:身份证、护照
记得检查这些证件的有效期!
用的全是老技术:字典计数(第二十二篇)、字符串乘法画图(第二十一篇进度条)、列表推导式筛选(第十二篇)。但组合出来,它会主动提醒你该干的事——这就不是练习,是工具了。
十、第三个改进:导出报表 — 生成可打印的清单
最后加一个实用功能:把档案导出成一个人能看的文本文件,可以打印出来锁进抽屉,或者发给自己备份。
def 导出报表():
"""把档案导出成格式化的文本文件(号码部分隐藏)"""
if not 档案列表:
print("📭 没有档案可导出")
return
from datetime import datetime
报表名 = f"档案清单_{datetime.now().strftime('%Y%m%d')}.txt"
with open(报表名, "w", encoding="utf-8") as f:
f.write("个人档案清单n")
f.write(f"导出时间:{datetime.now().strftime('%Y-%m-%d %H:%M')}n")
f.write("=" * 50 + "nn")
for 类别 in 类别表:
该类 = [条 for 条 in 档案列表 if 条["类别"] == 类别]
if not 该类:
continue # 空类别跳过
f.write(f"【{类别}】n")
for 条 in 该类:
# 号码中间打码:只显示前3位和后2位
号 = 条["号码"]
if len(号) > 6:
隐藏 = 号[:3] + "*" * (len(号) - 5) + 号[-2:]
else:
隐藏 = "****"
f.write(f" {条['名称']:<12} {隐藏}")
if 条["备注"]:
f.write(f" ({条['备注']})")
f.write("n")
f.write("n")
f.write("=" * 50 + "n")
f.write(f"共 {len(档案列表)} 条档案n")
print(f"📄 报表已导出:{报表名}")
三个值得注意的细节:
1. 导出时打码。 号[:3] + "*" * (len(号) - 5) + 号[-2:]——只露前3位和后2位,中间全遮住。报表是给人看的、可能打印出来的,不能带完整敏感信息。切片操作(第八篇)在这里派上用场。
2. 空类别跳过。 if not 该类: continue——没有”金融”类档案就别印空标题。报表的每一行都该有信息量。
3. 文件名带日期。 档案清单_20250115.txt——导出多次不会互相覆盖,每次都是独立的快照。
导出的报表长这样:
个人档案清单
导出时间:2025-01-15 20:30
==================================================
【证件】
身份证 110********234 (2030年到期)
护照 E12****56 (2028年到期)
【账号】
常用邮箱 ***
==================================================
共 5 条档案
打印出来锁抽屉,应急时翻出来——这就是”数字工具服务现实生活”。
十一、阶段2毕业总结
看看这个不到200行的程序,用了多少东西:
| 知识点 | 在哪里用的 |
|---|
|——–|———–|
| 变量和数据类型 | 档案字段、各种状态 |
|---|---|
| 条件判断 | 菜单分发、输入验证、打码判断 |
| 循环 | 主循环、遍历档案、分类统计 |
| break/continue | 退出、跳过空类别 |
| 函数 | 每个功能一个函数、辅助函数 |
| 函数作为值 | 字典装菜单函数 |
| return / None | 按名称找的两种返回 |
| 列表 | 档案列表、筛选结果 |
| append / remove / in | 增删查 |
| 字典 | 档案、计数、菜单 |
| 字典.get | 累加计数、默认值 |
| 列表推导式 | 按类别筛选、按条件搜索 |
| 切片 | 号码打码 |
| 字符串方法 | strip、startswith、join、字符串乘法 |
| 文件操作 | JSON档案、TXT报表 |
| 错误处理 | 输入验证、文件不存在、文件损坏 |
| import | json、os、datetime |
| ord/chr | 加密解密 |
| global | 加载时改全局列表 |
| f-string格式化 | 对齐表格、日期文件名 |
19类知识点,没有一个是阶段2新学的。 全部来自阶段1的12篇文章。
这就是今天最重要的结论:你不需要新知识,就能做出真正有用的软件。 阶段2的12个项目(13-24),每个都在告诉你同一件事——
- 猜数字:条件+循环就能做游戏
- 密码生成器:随机+字符串就是安全工具
- 记账本:字典+统计就能管钱
- 番茄钟:time+循环就能管时间
- 档案管理器:全拼起来就是完整软件
知识还是那些知识,变的是你组合它们的能力。
十二、跟AI协作的进阶心得
这12个项目做下来,你应该已经摸到了跟AI协作的节奏。总结几条:
1. 先设计数据,再要代码。 这一篇我们先想清楚”一条档案是什么字典”,再去问AI。数据结构定了,代码怎么写都顺。反过来(先要代码再理解数据)会一直磕磕绊绊。
2. 大项目拆成小步。 档案管理器如果一次性让AI全写出来(含加密、报表),代码几百行,你根本读不完。先做主体,跑通了再一个个加功能——每一步的代码量都是你能消化的。
3. 改动前先读懂现有代码。 加密功能动了保存和加载两个函数。如果没读懂原来的代码,改起来必然出错。读懂再改,永远比”让AI重写”靠谱——重写会把你已经调好的其他部分弄坏。
4. 报错信息是你的朋友。 这12个项目里你肯定遇到过报错。养成习惯:把完整报错信息发给AI,说明你做了什么。90%的错误AI能直接定位。
十三、今日小练习
毕业练习,三个都做完,阶段2圆满:
练习1:主密码锁
程序启动时要求输入主密码,密码不对不让进。把主密码加密存在一个单独的小文件里,第一次使用时设置。(提示:复用加密文本函数;用input+比对;错误三次自动退出——循环+计数器。)
练习2:修改档案时支持改类别
现在的修改功能只能改号码和备注。加上改类别(重新显示类别菜单)。再想想:能不能让用户输”直接回车不改”来跳过?现在的代码已经是这个逻辑了,类别这栏怎么套进去?
练习3:合并两个档案文件
写一个”导入”功能:从另一个JSON档案文件读入档案,合并到当前列表里。注意处理重名——同名档案是跳过、覆盖,还是都保留改个名?三种策略选一种实现。(提示:加载档案函数改造一下,别用global,改成传参数返回结果更干净。)
写在阶段2的结尾
回头看第一篇,你还在问”编程是什么”。现在,你已经有了12个自己亲手做出来的程序——游戏、工具、计时器、管理器。每一个都是从”跟AI聊需求”开始,一行行读懂,一步步改进出来的。
你已经不是”学编程的人”了,你是”会编程的人”。 这两者之间的区别不是知识量,而是——前者总觉得”我还没准备好”,后者知道”我可以开始做了”。
阶段3(第25篇起)要爬第一个真正的台阶了:文件和数据处理——CSV、JSON、数据清洗、批量处理。有了阶段2这12个项目的肌肉记忆,那个台阶你上得去。
去造下一个工具吧。
发表回复