几十个 Excel/CSV 按某一列拆分成多个文件(含 4 个实跑大坑)

行政、HR、财务常遇到这种活:3 个分公司交来的员工表,编码不一样、列名有的带空格,要按部门拆成一人事一份。下面这份脚本本次完整实跑过:3 个混合编码(utf-8-sig / gbk / utf-8)、表头带尾随空格、部门值带前导空格的脏文件,全部正确拆进 4 个部门文件,重跑幂等。

核心脚本

import csv, glob, os, codecs

def sniff_encoding(path):
    raw = open(path,'rb').read()          # 坑1:必须读全文件,见下文
    if raw.startswith(codecs.BOM_UTF8): return 'utf-8-sig'
    for enc in ('utf-8', 'gb18030'):      # 坑2:gb18030 是 gbk 超集
        try:
            raw.decode(enc); return enc
        except UnicodeDecodeError:
            continue
    return 'latin-1'

DEPT_COL = '部门'
groups = {}
for path in glob.glob('员工表*.csv'):
    enc = sniff_encoding(path)
    with open(path, encoding=enc, newline='') as f:
        rdr = csv.DictReader(f)
        rdr.fieldnames = [k.strip() for k in rdr.fieldnames]  # 坑3:表头可能带尾随空格
        for r in rdr:
            r = {(k or '').strip():(v or '').strip() for k,v in r.items()}
            dept = r.get(DEPT_COL) or '未知部门'
            r['来源文件'] = os.path.basename(path)
            groups.setdefault(dept, []).append(r)

os.makedirs('按部门', exist_ok=True)
used = set()
for dept, rows in groups.items():
    name = dept.replace('/','_')
    out = f'按部门/{name}.csv'; n = 2
    while out in used: out = f'按部门/{name}_{n}.csv'; n += 1   # 防同名覆盖
    used.add(out)
    with open(out,'w',encoding='utf-8-sig',newline='') as f:  # 坑4:见下文
        w = csv.DictWriter(f, fieldnames=['姓名','部门','工资','来源文件'])
        w.writeheader(); w.writerows(rows)
    print(f'{out}: {len(rows)} 行')

实跑结果

构造的 3 个脏源文件(共 9 条记录):

拆分输出全部正确:

按部门/技术部.csv: 4 行   (张三/李四/赵六/周九,跨 2 个源文件正确归并)
按部门/市场部.csv: 2 行   (王五/钱七)
按部门/人事部.csv: 1 行   (孙八)
按部门/财务部.csv: 1 行   (吴十)

重跑一遍,行数不变——输出写到独立目录、源文件只读,天然幂等。

4 个实跑大坑

坑 1:编码探测只读前 4 字节会翻车。第一版 raw = open(path,'rb').read(4) 后拿完整内容去 decode,结果第 3 个文件判成 gbk 直接 UnicodeDecodeError。原因:只验了前 4 字节合法就猜对编码。修法是读全文件再做 decode 验证,宁可多花几毫秒。
坑 2:用 gb18030 而不是 gbk。gb18030 是 gbk 的超集,能多接住一批生僻字和扩展区汉字,容错更好。探测顺序:BOM → utf-8 → gb18030。
坑 3:表头和值都要 strip。真实表格里「部门 」和「 部门」都见过,不做归一化就会拆出两个「部门」组。除了 DictReader 的 fieldnames,每行的 key/value 也要再 strip 一次,前导空格的「 技术部」才能正确归并进技术部。
坑 4:输出一定用 utf-8-sig。纯 utf-8 的 CSV 在 Windows 上双击用 Excel 打开中文全是乱码;带 BOM(utf-8-sig)才能被 Excel 正确识别。给非程序员的交付物,这一条是底线。另外部门名里若有 / 要替换掉,否则当文件名直接报错。

要拆的是 Excel(.xlsx)怎么办?

把读入部分换成 pandas.read_excel(path, dtype=str)(多个 sheet 加 sheet_name=None 遍历),分组逻辑不变。注意 dtype=str,否则工号、手机号会被 pandas 读成数字丢前导零。

没时间自己改?这是 喵窝资源铺 的常用脚本场景之一:9.9 元起,把你表格发来,AI 写脚本+人工核验结果后交付,虚拟物品不退款但免费重做到结果正确。更多实跑验证过的脚本见 脚本合集。