简介:这是一份基于 Java 与 MySQL 的通讯录管理系统课程设计完整项目,主要面向计算机专业学生、Java 初学者以及需要完成课设或实训报告的人群。整个资源压缩包共包含 51 个文件,大小仅为 3.64MB,涵盖 6 个 Java 源文件、30 个 class 编译文件、6 个 XML 工程配置、3 个 CSV 示例数据、1 个 MySQL 驱动 JAR 包和 Word 版实验报告。包内目录划分清晰,既有可直接导入开发环境的源码与依赖配置,也有编译输出和数据库数据样例,能够完整支撑从环境搭建、编码实现到功能测试的整个流程。目前该资源已有 573 人学习下载。学习这份项目,可以掌握 Swing 界面开发、JDBC 数据库连接、通讯录的增删改查操作,以及 CSV 数据的导入导出方法;配套实验报告还梳理了数据库表结构设计和系统实现思路,适合作为课程设计模板、答辩讲解或 Java 综合练手项目。
1. AddressBook.zip 不只是"通讯录压缩包"这么简单
把AddressBook.zip拆开看,它是通讯录数据与 zip 容器两个问题的叠加。在真实环境里,它可能是手机导出的 vCard 备份、CRM 系统的联系人导出档,也可能是某个离职员工留下的客户资料包。拿到手,有人双击解压却发现中文乱码,有人输入口令死活不对,有人解压完面对一堆 .vcf 不知道下一步怎么导入系统。这里顺着完整链路往下走:解压前怎么摸清 zip 包元数据、vCard 到 CSV 的字段映射怎么做、zip 密码恢复和损坏修复的参数怎么选,以及最后批量导入时怎么验证结果不出错。适合要做通讯录迁移、数据清洗或系统对接的开发和运维人员,也让第一次拿到这种包的初级工程师能顺着步骤走通。
2. 解压前的元数据检查:用 7-Zip 和 zipfile 看清包内结构
2.1 7z l 先看再解,避免解压后才发现问题
拿到 AddressBook.zip 不建议直接用鼠标双击解压到当前目录。用 7-Zip 的命令行工具先做一次透视,能看到包内文件的全貌:
7z l AddressBook.zip输出里包含每个文件项的完整路径、修改时间、原始大小、压缩后大小和 CRC 校验值。对这份输出要特别注意两件事:一是路径层级,很多通讯录导出包在压缩时没做路径清理,解压后会出现contacts/2024/2024-11-08/export/contacts.vcf这类嵌套结构;二是文件数量,如果包里有几百个 vcf 文件,后续解析就要写成批量脚本而不是单文件处理。
确认结构之后正式解压:
7z x AddressBook.zip -o./ab_export -y-o指定输出目录,注意-o后面不能有空格;7z x默认保留包内目录结构,-y表示遇到重名文件自动覆盖。这里我一般不用7z e,因为e会把所有文件平铺到一个目录里,多个子目录下的同名文件会被互相覆盖,通讯录分组信息也会随之丢失。
2.2 Python zipfile 模块:不落盘读取包内容和元数据
在自动化脚本或 CI 流程里,先解压再读取不是一个好习惯。用 Python 标准库 zipfile 在内存里完成检查更干净:
import zipfile with zipfile.ZipFile('AddressBook.zip') as zf: for info in zf.infolist(): print(f"{info.filename}\t{info.file_size}\tCRC={info.CRC:08x}")infolist()返回的是每个文件条目的 ZipInfo 对象,包含文件名、压缩前后大小、CRC、时间戳等字段。这段代码的意义在于解压任何数据之前就能发现三个常见问题:包内是否有非通讯录文件混入、是否有异常大的条目(比如联系人照片被打包进去)、文件名是否在控制台显示为乱码。
zipfile 还能直接按文件名读取压缩条目内容,完全不必落地到磁盘:
import zipfile with zipfile.ZipFile('AddressBook.zip') as zf: with zf.open('contacts/export/contacts.vcf') as f: content = f.read().decode('utf-8-sig')zf.open()返回一个类似文件对象的实例,read()是一次性读入全部字节。对 vCard 这类文本格式足够用了;如果 AddressBook.zip 里打包了带照片的联系人,图片文件在几十 KB 到几 MB 之间,这时更稳妥的做法是zf.open()配合f.read(65536)分块读取,避免单次分配过大内存。
表格:zipfile 核心方法适用边界
| 方法 | 作用 | 适用场景 |
|---|---|---|
namelist() | 返回全部文件名列表 | 快速扫描包内有哪些条目 |
infolist() | 返回 ZipInfo 对象列表 | 需要大小、CRC、时间戳时 |
read(name) | 按名一次性读取全部字节 | 小文本文件、快速验证 |
open(name) | 按名返回文件对象 | vCard、图片等逐块处理 |
extractall(path) | 解压全部到指定目录 | 已确认安全后的落地动作 |
2.3 中文文件名的编码还原:cp437 回退与 GBK 解码
Windows 自带压缩工具和一些老版本压缩软件生成 zip 时,文件名用 GBK 编码但没有在 zip 头里标注 UTF-8 标志位。Python zipfile 对这类文件默认按 CP437 字符集解码,直接打印就是乱码。
先写个小脚本确认当前 zip 文件用的是哪种编码:
import zipfile with zipfile.ZipFile('AddressBook.zip') as zf: for name in zf.namelist(): print(repr(name))如果输出中出现'\xd5\xc5\xce\xb0.vcf'这样的字符序列,说明原始编码是 GBK。还原文件名要分两步:先用 cp437 把它还原成压缩时写入的原始字节,再按 GBK 解码:
raw_bytes = name.encode('cp437') real_name = raw_bytes.decode('gbk') print(real_name) # 张伟.vcf提示:这个技巧同样适用于解压后的文件内容。vCard 文件头没有强制编码声明,内容乱码时先用同样的 cp437→GBK 还原思路定位问题。
这个编码细节在通讯录场景里几乎是必踩的坑,中文姓名是通讯录最核心的数据项,文件名和内容双重乱码会让整个导入流程直接报废。
3. AddressBook 数据解析:vCard 转 CSV 的字段映射与编码清洗
3.1 三种通讯录存储格式怎么识别
解压完成后,面对.vcf、.csv、.json三种常见格式,第一步是识别而不是无脑解析。
表格:通讯录数据格式速查
| 格式 | 扩展名 | 典型来源 | 结构化程度 |
|---|---|---|---|
| vCard | .vcf | iOS/Android 通讯录导出 | 块结构,字段有序 |
| CSV | .csv | 企业 CRM 导出 | 表格型,字段顺序取决于模板 |
| JSON | .json | API 导出 | 树结构,键值对自由 |
vCard 是最常见也最容易解析出错的一种。它不是一行一个字段的扁平结构,而是由BEGIN:VCARD与END:VCARD包裹的多行块。每个属性由"参数:值"构成:
BEGIN:VCARD VERSION:3.0 FN:张伟 N:张;伟;;; TEL;TYPE=CELL:+86 13800138000 EMAIL;TYPE=WORK:zhangwei@example.com END:VCARD注意TEL行里的TYPE=CELL是电话类型标记,EMAIL行里的TYPE=WORK表示工作邮箱。直接按:做 split 的粗糙解析会丢掉这些元数据,后续按"手机号/座机/工作邮箱/个人邮箱"分列的导入需求就会落空。
3.2 用 vobject 把 vCard 解析成结构化数据
解析 vCard 不推荐手写正则,因为 2.1、3.0、4.0 三个版本的字段写法各有差异,数据量大时正则很容易崩。用 vobject 这个 Python 库可以屏蔽掉大部分版本差异:
import csv from vobject import readComponents def vcf_to_csv(vcf_path, csv_path): records = [] with open(vcf_path, 'r', encoding='utf-8-sig') as f: content = f.read() for vcard in readComponents(content): name = str(vcard.fn.value) if hasattr(vcard, 'fn') else '' tels = [] if hasattr(vcard, 'contents') and 'tel' in vcard.contents: for tel in vcard.contents['tel']: tels.append(str(tel.value)) emails = [] if hasattr(vcard, 'contents') and 'email' in vcard.contents: for email in vcard.contents['email']: emails.append(str(email.value)) records.append([name, ';'.join(tels), ';'.join(emails)]) with open(csv_path, 'w', encoding='utf-8-sig', newline='') as f: writer = csv.writer(f) writer.writerow(['姓名', '电话', '邮箱']) writer.writerows(records) vcf_to_csv('contacts.vcf', 'contacts.csv')readComponents返回一个生成器,逐个 yield 文档中的联系人对象。用hasattr(vcard, 'fn')判断联系人是否有姓名属性,避免拿到只有电话没有姓名的残缺记录时报 AttributeError。'tel' in vcard.contents的写法比直接访问vcard.tel更安全,因为单个联系人有多个电话号码时,vcard.tel只暴露第一条,contents字典里存的才是全部条目。
多个电话用;合并进同一格,是为了保持 CSV 的行数与联系人一一对应。如果目标导入系统只接受一个电话字段,这种合并方式最不容易丢数据。
3.3 中文乱码检测与转码:chardet 加置信度判断
vCard 文件内容是通讯录解析的另一个重灾区。iOS 导出的 vcf 通常是 UTF-8,但 Windows 上 Outlook 通讯录导出、国内不少 CRM 系统导出的 vcf 却用 GBK/GB2312 编码。脚本写死encoding='utf-8'会在读取阶段直接抛异常,或者更糟——读进来了但内容全是乱码。
处理这类问题我习惯先用 chardet 检测编码,再根据置信度决定怎么解码:
import chardet def read_text_smart(filepath): with open(filepath, 'rb') as f: raw = f.read(4096) result = chardet.detect(raw) print(f"编码: {result['encoding']}, 置信度: {result['confidence']}") if result['confidence'] > 0.7: return raw.decode(result['encoding']) return raw.decode('utf-8', errors='replace')只读前 4096 字节做检测,是因为编码判断依赖的是字节分布特征,文件头部的样本量足够让 chardet 给出可靠结论。置信度阈值设在 0.7 是我的习惯——低于这个值说明文件可能经过多次转码或者混入了其他编码的字节,自动解码有风险,宁可打日志让下游人工确认。
这里有个容易忽略的细节:UTF-8 编码的文件常带 BOM 头(\xef\xbb\xbf),如果检测结果显示UTF-8-SIG却用utf-8去解,BOM 会作为不可见字符留在第一条记录的姓名前面,导入系统后肉眼很难发现,但会导致按姓名去重失败。所以 3.2 节代码里读 vcf 时用utf-8-sig,就是为了把 BOM 干净地剥掉。
4. zip 密码恢复与 CRC 损坏修复:工具参数与操作顺序
4.1 先把加密类型分清楚:ZipCrypto 和 AES-256 难度完全不同
遇到带密码的 AddressBook.zip,第一步不是急着破解,而是判断加密算法。用 7-Zip 打开文件时,"加密方式"一栏会标出ZipCrypto或AES-256。这两种算法在暴力破解时的计算量不在一个数量级。
表格:常见 zip 密码恢复工具能力对比
| 工具 | ZipCrypto | AES-256 | GPU 加速 | 适用场景 |
|---|---|---|---|---|
| fcrackzip | 支持 | 不支持 | 否 | 快速测试弱口令、短密码 |
| John the Ripper | 支持 | 支持 | 是 | 字典加规则、混合攻击 |
| hashcat | 支持 | 支持 | 是 | GPU 暴力与掩码攻击 |
| 图形界面类工具 | 支持 | 部分支持 | 部分 | 不熟悉命令行的临时场景 |
ZipCrypto 算法设计较弱,即使密码较长,在 GPU 环境下也能在合理时间内跑完;AES-256 则没有捷径,只能靠密码本身的强度。所以拿到加密包先看算法,如果是 AES-256,优先考虑找回密码而不是暴力破解。
4.2 fcrackzip 与 hashcat 的实操命令
假设这个包确定是 ZipCrypto,密码是 4 到 8 位纯数字,先用 fcrackzip 做暴力试探:
fcrackzip -u -b -l 4-8 -p 0 AddressBook.zip-u表示对每个候选密码做实际解压并校验 CRC,-b进入暴力模式,-l 4-8限定密码长度,-p 0从数字 0 开始尝试。这里必须加-u:不加时 fcrackzip 只验证 zip 头信息的指纹,有可能报出假密码;加-u才做真正的解密尝试,结果可靠但速度会慢一些。
如果手头有一份行业常见的密码字典,先跑字典模式,通常比暴力模式快一个数量级:
fcrackzip -u -D -p passwords.txt AddressBook.zip-D切换到字典模式,-p后接字典文件路径。通讯录备份的密码往往来自企业安全策略,比如"姓名缩写+工号"这类组合,好的字典能直接命中。
字典跑空之后,上 GPU 掩码攻击。先把 zip 的 hash 提取出来,再交给 hashcat:
zip2john AddressBook.zip > ab_hash.txt hashcat -m 17225 ab_hash.txt -a 3 ?u?l?l?l?d?d?d?d-m 17225对应当前 ZipCrypto Master Key 攻击模块,-a 3是掩码攻击,?u表示大写字母,?l小写字母,?d数字。这个掩码假设密码是"大写字母开头 + 3 个小写字母 + 4 位数字"的 8 位密码,是一种常见的企业默认密码规则。跑掩码前先确认密码规律至关重要,一个合理的掩码可以把暴力破解的天数缩短到小时。
注意:如果是 7-Zip 创建的 AES-256 加密包,hash 要用 7z2john 提取,hashcat 的模块编号对应
-m 11600。用-m 17225跑 7z 的 hash 只会得到无用的输出。
4.3 error read zip archive 与 CRC failed 的处理顺序
解压时报error read zip archive或CRC failed是另一个高频问题。这两个报错含义不同:前者指 zip 文件结构层面损坏,多为下载中断、拷贝不完整导致中央目录缺失;后者表示压缩数据区有字节损坏或密码不对。
处理顺序不能乱。先用完整性测试排除密码因素:
7z t AddressBook.zip -pYOUR_PASSWORD7z t是完整性测试模式,带密码逐条校验所有文件条目。输出中Everything is Ok表示数据完好;如果列出某个文件名并伴随CRC Failed,说明该文件的数据区间确实出了问题。
对结构损坏的 zip,可以用zip -F尝试重建中央目录:
zip -F AddressBook.zip --out AddressBook_fixed.zip-F模式下 zip 工具会扫描整个文件,尝试重建索引。它救不了压缩数据本身损坏的文件,但对"中央目录缺失但数据区完整"的场景效果显著。修复后务必再用7z t做一次完整性测试,确认逐条文件都通过校验之后再进入解压环节。
如果-F也无法重建,用7z e把还能解的文件全部抽出。这个方案虽然笨,但面对高价值的通讯录历史数据,每抢回一条记录都值得。
5. AddressBook 批量导入前的校验与三个验证维度
5.1 解压与校验用一条命令完成
批量导入之前先做一遍解压结果校验,省得导入系统后才发现中间文件已经损坏:
7z x AddressBook.zip -o./ab_export -y 7z t AddressBook.zip7z x完成解压,7z t对压缩包做整体完整性校验。解压出的文件数量与7z l里看到的条目数不一致时,说明有文件在解压中被跳过,最常见的原因是文件名编码冲突或磁盘空间不足,先解决这两个问题再继续。
5.2 批量导入字段映射的脚本骨架
企业通讯录导入系统(飞书、钉钉、Exchange 或自研后台)通常都接受 CSV,但字段名各家有各家的叫法。从 vCard 解析出的"通用 CSV"往往和系统模板对不上:
import csv FIELD_MAPPING = { '手机号': '电话', '姓名': '姓名', '公司邮箱': '邮箱', } with open('contacts.csv', 'r', encoding='utf-8-sig') as f: reader = csv.DictReader(f) with open('import_ready.csv', 'w', encoding='utf-8-sig', newline='') as out: writer = csv.DictWriter(out, fieldnames=list(FIELD_MAPPING.keys())) writer.writeheader() for row in reader: mapped = {dest: row.get(src, '') for dest, src in FIELD_MAPPING.items()} writer.writerow(mapped)FIELD_MAPPING字典的键是目标系统的字段名,值是通用 CSV 的字段名。实现字段重命名的核心是{dest: row.get(src, '')}这一句,get加默认空字符串可以防止源 CSV 缺列时抛 KeyError。映射完的文件直接就是目标系统的导入模板格式,省掉了"导出、手动改表头、再导入"的中间步骤。
5.3 导入后抽样验证的三个维度
批量导入完成后,目标系统提示的"导入成功 N 条"不能全信。通讯录的准确性取决于电话和邮箱两个字段,我习惯做三个维度的抽样核对。
一是数量对比,数一下源 vCard 里 TEL 字段的实际条数,和导入系统的联系人记录数做比对:
grep -c "^TEL" contacts.vcf二是格式检查,抽样 20 条确认国内手机号是否保留了+86前缀、座机号码是否带区号、有没有空姓名记录。三是字段错位检查,拿源 vCard 里的前 5 个联系人姓名去目标系统搜索,逐个确认电话和邮箱能对上。
三个维度核对完,这套解压、解析、导入、验证的链路才真正走完。下次再遇到带编码问题和密码问题的 AddressBook.zip,先7z t验包,再用 cp437→GBK 还原文件名,能避开一大半的坑。
本文还有配套的精品资源,点击获取