Day 063 — CSV 与 Excel:图解
1. CSV 数据流
CSV 文件 (.csv) 内存中 Python 对象
┌──────────────────┐ ┌──────────────────────┐
│ name,age,city │ reader() │ ['name', 'age', 'city']│
│ Alice,30,Beijing │ ────────→ │ ['Alice', '30', 'Beijing']│
│ Bob,25,Shanghai │ │ ['Bob', '25', 'Shanghai']│
│ Charlie,35,SZ │ │ ['Charlie', '35', 'SZ']│
└──────────────────┘ └──────────────────────┘
│ │
│ DictReader() DictWriter()
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ [{'name':'Alice',│ │ 字符串 → 数字转换 │
│ 'age':'30', │ │ json.loads → 类型推导 │
│ 'city':'Beijing'}, ...] │ 日期解析 → datetime │
└──────────────────┘ └──────────────────────┘
2. Excel 文件结构 (基于 OOXML)
report.xlsx (ZIP - 可以 unzip 查看!)
│
├── [Content_Types].xml ← 内容类型声明
│
├── _rels/.rels ← 根关系映射
│
├── xl/
│ ├── workbook.xml ← 工作簿定义 (sheet 列表)
│ │
│ ├── worksheets/
│ │ ├── sheet1.xml ← 第 1 个工作表 (XML 格式数据)
│ │ └── sheet2.xml ← 第 2 个工作表
│ │
│ ├── sharedStrings.xml ← 共享字符串表 (优化重复文本)
│ ├── styles.xml ← 样式定义 (字体/颜色/边框)
│ ├── theme/ ← 主题定义
│ │ └── theme1.xml
│ │
│ └── charts/
│ └── chart1.xml ← 图表定义
│
├── docProps/
│ ├── app.xml ← 应用属性
│ └── core.xml ← 核心属性 (作者/日期)
3. 数据读取路径对比
CSV / Excel 数据
│
┌────────────┴────────────┐
│ │
csv 模块 / openpyxl pandas
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ 逐行读取 │ │ 批量加载到内存 │
│ 低内存消耗 │ │ 自动类型推断 │
│ 手动类型转换 │ │ 丰富的数据操作 API │
│ │ │ │
│ ✅ 适合超大文件 │ │ ✅ 适合数据分析 │
│ ✅ 精确控制格式 │ │ ✅ 一行代码搞定 │
│ ❌ 代码繁琐 │ │ ❌ 大文件内存占用高 │
│ ❌ 无类型推断 │ │ ❌ 依赖重 │
└──────────────────────┘ └──────────────────────┘
4. CSV 解析状态机
编写正确的 CSV 解析器比看起来复杂得多:
┌─────┐
│ 开始 │
└──┬──┘
│ 读取字符
▼
┌───────────┐
┌──→│ 字段开始 │←────────────────────┐
│ └─────┬─────┘ │
│ │ 遇到引号? │
│ ┌────┴────┐ │
│ │ 是 │ 否 │
│ ▼ ▼ │
│ ┌──────┐ ┌──────┐ │
│ │引号内 │ │普通 │ │
│ └──┬───┘ └──┬───┘ │
│ │ │ │
│ │ 双引号? │ 分隔符? │
│ ├──是──┐ ├──是──→ 字段结束 │
│ │ │ │ │
│ │ 否 │ │ 换行? │
│ │ │ ├──是──→ 记录结束 │
│ ▼ │ │ │
│ ┌──────┐ │ │ │
│ │引号结束│←┘ │ │
│ └──┬───┘ │ │
│ │ │ │
└────┘────────┘ │
│
┌──────────────────────────────┘
│ 遇到 escapechar?
▼
┌───────────┐
│ 转义处理 │
└───────────┘
5. Excel 样式体系
Excel 单元格
│
├── Font (字体)
│ ├── name='Microsoft YaHei'
│ ├── size=12
│ ├── bold=True
│ ├── color='4472C4'
│ └── italic=False
│
├── Fill (填充)
│ ├── PatternFill(start_color='4472C4', fill_type='solid')
│ └── GradientFill(...)
│
├── Alignment (对齐)
│ ├── horizontal='center'
│ ├── vertical='center'
│ └── wrap_text=True
│
├── Border (边框)
│ ├── left=Side(style='thin', color='D9E2F3')
│ ├── right=Side(style='thin')
│ ├── top=Side(style='thin')
│ └── bottom=Side(style='thin')
│
├── Number Format (数字格式)
│ ├── '#,##0' → 千分位整数
│ ├── '¥#,##0.00' → 货币格式
│ ├── '0.00%' → 百分比
│ └── 'YYYY-MM-DD' → 日期格式
│
└── Protection (保护)
└── locked=True/False
6. 报表生成流程(实战)
原始数据 (CSV)
│
▼
┌─────────────┐ ┌───────────────┐
│ 数据加载 │────→│ ReportGenerator │
│ csv.DictReader│ │ .clean_data() │
└─────────────┘ │ .analyze() │
└───────┬───────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Sheet 1 │ │ Sheet 2 │ │ Sheet 3 │
│ 数据明细 │ │ 汇总统计 │ │ 图表 │
│ 原始数据 │ │ 分组汇总 │ │ 柱状图 │
│ 带样式 │ │ 条件格式 │ │ 饼图 │
└──────────┘ └──────────┘ └──────────┘
│ │ │
└─────────────┼─────────────┘
│
▼
┌─────────────┐
│ Final.xlsx │
└─────────────┘