重复打开表格、按类别相加、再检查有没有漏项,是很适合交给脚本的工作。本篇使用 Python 标准库,把一份 CSV 支出示例汇总为分类金额。重点是明确输入格式、校验错误和给出可复核的输出,而不是堆砌第三方库。
准备一份可核对的输入
需要 Python 3.9 或更高版本。新建 expenses.csv,保存为 UTF-8。第一行是字段名,其余每行是一条记录。这里仅演示单一币种、最多两位小数的非负支出;退款、多币种和税务处理不在这个小脚本的范围内。
category,amount
餐饮,18.50
交通,3.00
餐饮,26.00
学习,49.90
交通,6.00CSV 不等于“随便用逗号切字符串”。字段可能包含逗号、引号或换行,交给标准库读取更稳妥。表格软件导出的 UTF-8 文件可能带 BOM,因此示例使用 utf-8-sig 读取,兼容带 BOM 和不带 BOM 的 UTF-8。
完整脚本:读取、校验、汇总
把下列代码保存为同目录的 summarize.py。金额使用 Decimal 从原始字符串创建,避免把二进制浮点数的近似值带入计算。示例拒绝负数、无限值和超过两位的小数,并在遇到错误时停止,不跳过问题记录后继续报一个不完整的总额。
import argparse
import csv
import re
from collections import defaultdict
from decimal import Decimal
from pathlib import Path
def summarize(path):
totals = defaultdict(Decimal)
with path.open(encoding='utf-8-sig', newline='') as stream:
reader = csv.DictReader(stream)
if reader.fieldnames != ['category', 'amount']:
raise ValueError('表头必须依次为 category,amount')
for record, row in enumerate(reader, start=1):
# 不静默丢弃多余字段或缺失字段。
if None in row or any(value is None for value in row.values()):
raise ValueError(f'第 {record} 条记录:字段数量不正确')
category = row['category'].strip()
amount = row['amount'].strip()
if not category:
raise ValueError(f'第 {record} 条记录:类别为空')
if not re.fullmatch(r'[0-9]{1,9}(\.[0-9]{1,2})?', amount):
raise ValueError(f'第 {record} 条记录:金额格式不正确')
totals[category] += Decimal(amount)
return totals
def main():
parser = argparse.ArgumentParser(description='按类别汇总 CSV 示例')
parser.add_argument('file', type=Path)
args = parser.parse_args()
try:
totals = summarize(args.file)
except (OSError, UnicodeError, csv.Error, ValueError) as error:
parser.exit(1, f'处理失败:{error}\n')
if not totals:
print('没有可汇总的记录。')
return
for category, amount in sorted(totals.items()):
print(f'{category}: {amount:.2f}')
total = sum(totals.values(), Decimal('0'))
print(f'合计: {total:.2f}')
if __name__ == '__main__':
main()运行并核对结果
在文件所在目录运行下面的命令。Windows 的 Python 启动器可使用 py -3 summarize.py expenses.csv。文件名含空格时,用引号包住路径。
python3 summarize.py expenses.csv对本篇给出的五条数据,预期输出如下。先手工验证一次:餐饮 18.50 + 26.00 = 44.50,交通 3.00 + 6.00 = 9.00,再加学习 49.90,合计应为 103.40。
交通: 9.00
学习: 49.90
餐饮: 44.50
合计: 103.40故意制造错误,确认脚本会拒绝它
把任意金额改成 12元、-3.00 或 1.234,应看到包含记录序号的错误提示,进程以失败状态结束。删去类别、增加一个多余字段、使用错误表头,也应失败。只有表头、没有数据时,应提示“没有可汇总的记录”。这里的序号指 CSV 数据记录,不是包含换行字段时的物理文本行号。
如果中文乱码,先检查 CSV 的真实编码;换一个读取编码只是兼容输入,不能把损坏的文本自动修复。脚本不会修改原文件,便于你对照源数据重新核算。
如何继续扩展
先增加日期字段和按月筛选,再考虑导出汇总 CSV。每次增加功能,都保留一份小而可手工计算的样本。若要支持退款或多币种,应先写清业务规则,再改校验逻辑,不能只删除负数检查就认为问题已经解决。
继续查阅
读取方式与换行处理参见 Python 官方文档:csv;金额计算可进一步阅读 Python 官方文档:decimal。示例数据仅用于编程练习。