资讯动态

批量Excel转CSV工具:Python脚本实现与避坑指南

发布时间:2026/10/24 22:52:35 来源:尧图企业网站定制
简介这是一款面向Windows平台、需要批量处理表格格式转换的办公与开发人员的实用工具核心功能是把指定目录下的xls与xlsx文件一次性全部转为csv省去逐个另存为的重复操作适合数据清洗、批量导入数据库或做脚本预处理的技术人员使用。压缩包共33个文件约94KB包含7个h头文件、5个cpp源文件以及csv、xls、xlsx示例数据另有exe可执行文件、vcproj与sln工程文件、rc资源脚本和ico图标等源码与工程配置齐全便于二次编译与调试。作者在原始源码基础上修复了与Windows SDK重定义Font类导致的编译失败问题并重新编译出ExcelToCsv.exe解压后在bin目录即可直接运行仅限Win系统作者已实测可用。目前已有2684人学习下载读者既能直接使用现成可执行文件完成批量转换也能通过源码研究Excel文件解析、目录遍历与MFC对话框实现思路对需要排错或改造转换逻辑的同学具有参考价值。1. 批量 Excel 转 CSV 工具从一堆 xlsx 里把数据捞干净手里压着几十上百个 xlsx、xls 报表每个都要另存为 csv 才能喂给下游脚本这种活我干过不止一次。手动点“另存为”点到手腕发酸还容易漏掉隐藏 sheet、合并单元格和日期格式这些暗雷。批量 Excel 转 csv 工具要解决的就是这件事把散落在多个工作簿里的表格按统一规则一次性转成 csv让后续的入库、比对、建模不再卡在格式上。它适合数据分析、报表清洗、ETL 前置处理这类场景尤其是那些每周都要重复导表的人。下面我按“先搞懂它怎么选、怎么跑再讲坑在哪”的顺序拆一遍新手能照着复现熟手能直接看参数边界。2. 选型与原理xlsx、xls、csv 到底差在哪2.1 三种格式的底层差异决定了转换策略xlsx 本质是一个 zip 包里面用 XML 描述工作表、共享字符串、样式和公式xls 是二进制复合文档结构更老也更封闭csv 则是纯文本只保留行列值不存类型、公式、颜色和合并信息。这意味着批量转换不是“改个后缀”那么简单而是要把带结构的工作簿降维成扁平文本。常见做法是先用读取库把每个 sheet 解析成二维表再按 csv 规则写出。选型时先看你的文件里有没有公式、多 sheet、合并单元格这三样直接决定你要不要额外处理。2.2 工具链怎么选pandas、openpyxl、xlrd 的边界Python 生态里最顺手的是 pandas 配 openpyxl读 xlsx和 xlrd读 xls注意新版 xlrd 只支持 xls。如果文件里有公式且你只要计算结果pandas 默认读缓存值如果要保留公式文本就得走 openpyxl 的 data_onlyFalse。批量场景我一般用 pandas 做主力因为它对缺失值、编码、列选择的控制最直接。下面这段是核心读取逻辑先看清楚再往下跑。import pandas as pd from pathlib import Path def read_one(path: Path, sheet_name0): # sheet_name0 表示只读第一个 sheet传 None 会读全部并返回字典 suffix path.suffix.lower() if suffix .xls: # xls 走 xlrd 引擎新版 pandas 需要显式指定 return pd.read_excel(path, sheet_namesheet_name, enginexlrd) # xlsx 默认 openpyxl显式写出来避免环境里引擎冲突 return pd.read_excel(path, sheet_namesheet_name, engineopenpyxl)逻辑说明sheet_name0是默认只取第一个工作表批量场景里如果每个文件结构一致这样最稳如果结构不一致改成None会返回一个字典键是 sheet 名值是对应 DataFrame后面再决定合并还是分别落盘。参数上engine必须显式指定否则在同时装了多个读取库的环境里pandas 可能选错引擎导致 xls 报错。这一步跑通后面才有资格谈批量。2.3 批量转换的目录约定与命名规则批量最怕的是输出文件重名。我一般约定输入目录按业务分文件夹输出目录保持同名结构文件名用“原文件名 sheet 名”拼接并把空格和特殊字符替换成下划线。这样即使两个工作簿里有同名 sheet也不会互相覆盖。下面这段是目录遍历和输出路径生成直接抄。import re from pathlib import Path def safe_name(name: str) - str: # 把 Windows/Linux 都不允许的字符替换掉避免落盘失败 return re.sub(r[\\/:*?|\s], _, name).strip(_) def iter_excel_files(root: Path): # 只取 xlsx 和 xls忽略临时文件 ~$ 开头的那种 for p in root.rglob(*): if p.suffix.lower() in (.xlsx, .xls) and not p.name.startswith(~$): yield p逻辑说明rglob(*)会递归子目录适合按业务分层的输入~$开头的是 Excel 打开时生成的临时文件不排除会读到空表或报错。safe_name里把空格也替换掉是因为有些下游系统对带空格的 csv 路径处理不友好。命名规则定好批量才不会变成“转完找不到文件”。3. 动手实现批量转换脚本与参数调优3.1 最小可用脚本一次转完整个目录先把最小闭环跑起来遍历目录、逐个读取、写出 csv。下面这段是完整可执行版本包含异常捕获避免一个坏文件打断整批。import pandas as pd from pathlib import Path def batch_convert(src_dir: str, dst_dir: str, sheet_name0, encodingutf-8-sig): src Path(src_dir) dst Path(dst_dir) dst.mkdir(parentsTrue, exist_okTrue) ok, fail 0, [] for path in iter_excel_files(src): try: df read_one(path, sheet_namesheet_name) # 相对路径保留目录结构输出到 dst 下同名位置 rel path.relative_to(src).with_suffix() out dst / rel.parent / f{safe_name(rel.name)}.csv out.parent.mkdir(parentsTrue, exist_okTrue) # indexFalse 不写行号encoding 用 utf-8-sig 兼容 Excel 打开 df.to_csv(out, indexFalse, encodingencoding) ok 1 except Exception as e: fail.append((str(path), repr(e))) return ok, fail逻辑说明encodingutf-8-sig是血泪经验普通 utf-8 在 Excel 里直接双击打开会中文乱码带 BOM 的 utf-8-sig 能避免这个问题indexFalse防止多出一列无名索引。fail列表记录失败文件和异常批量跑完先看这个列表比一个个翻输出目录快得多。参数上sheet_name默认 0如果你的工作簿每个 sheet 都要转传None并改成循环写出。3.2 多 sheet 与合并单元格的处理多 sheet 场景下sheet_nameNone返回字典遍历即可。合并单元格是另一个高频雷pandas 读进来只有左上角有值其余是 NaN。常见做法是先ffill()向下填充再决定是否保留。下面这段演示多 sheet 写出和合并单元格填充。def convert_all_sheets(path: Path, out_dir: Path): sheets pd.read_excel(path, sheet_nameNone, engineopenpyxl) for name, df in sheets.items(): # 合并单元格读进来是 NaN向下填充还原“合并”的语义 df df.ffill() out out_dir / f{safe_name(path.stem)}_{safe_name(name)}.csv df.to_csv(out, indexFalse, encodingutf-8-sig)逻辑说明ffill()是前向填充适合“某列只在合并区域第一行有值”的报表如果合并的是表头填充后可能产生重复列名需要再配合df.columns去重。参数上sheet_nameNone会一次性把所有 sheet 读进内存文件特别大时建议逐个 sheet 读避免内存爆掉。这一步做完多 sheet 报表才算真正转干净。3.3 大文件与编码参数怎么调文件大到几百 MB 时pandas 全量读入会吃内存。常见做法是分块读但 Excel 不像 csv 那样支持 chunksize所以更实际的是先确认单文件规模必要时用 openpyxl 的只读模式逐行取。编码方面输出统一 utf-8-sig输入如果遇到 xls 里的 GBK 文本pandas 一般能正确解码但极老的 xls 可能需要额外指定。下面是一个只读模式取值的思路适合超大 xlsx。from openpyxl import load_workbook def stream_xlsx(path: Path, out_csv: Path): # read_onlyTrue 不把整个工作簿加载进内存 wb load_workbook(path, read_onlyTrue, data_onlyTrue) ws wb.active with out_csv.open(w, encodingutf-8-sig, newline) as f: import csv writer csv.writer(f) for row in ws.iter_rows(values_onlyTrue): writer.writerow(row) wb.close()逻辑说明read_onlyTrue配合iter_rows是流式读取内存占用低data_onlyTrue取公式的计算结果而不是公式本身。参数上ws.active只取活动 sheet多 sheet 需要遍历wb.worksheets。这条路适合“文件巨大但结构简单”的场景代价是失去了 pandas 的类型推断和便捷筛选。4. 避坑与排查批量转换最常见的五个翻车点4.1 中文乱码现象、原因、解决现象是转出的 csv 用 Excel 双击打开全是乱码用记事本打开却正常。原因是 Excel 默认按本地编码解析无 BOM 的 utf-8。解决就是输出时用utf-8-sig让文件头带 BOM。如果下游系统不接受 BOM就输出纯 utf-8改由下游指定编码读取别两头都想讨好。4.2 日期变数字现象、原因、解决现象是日期列转出来变成 45000 这样的整数。原因是 Excel 内部用序列号存日期读取库没做转换。解决是在read_excel后对日期列显式pd.to_datetime或者读取时用dtype指定。更稳的做法是先看一眼前几行确认哪些列是日期再处理别全局强转。4.3 前导零丢失现象、原因、解决现象是工号、邮编这类带前导零的列转完零没了。原因是读取时被推断成数值。解决是读取时用dtypestr强制按文本读或者对特定列做zfill。批量场景我一般先读表头对疑似编码列统一按字符串处理。4.4 隐藏 sheet 和临时文件现象、原因、解决现象是输出目录里多出空 csv或者漏掉了本该转的 sheet。原因是~$临时文件被当成输入隐藏 sheet 被默认跳过。解决是遍历时过滤~$开头文件需要隐藏 sheet 时用 openpyxl 读wb.worksheets并判断sheet_state。4.5 同名覆盖现象、原因、解决现象是转完发现文件数比源文件少。原因是不同目录下的同名文件输出到了同一路径。解决是输出路径保留相对目录结构文件名拼接 sheet 名必要时加短哈希。批量转换的命名规则要在动手前定死中途改规则等于重跑。5. 进阶技巧把转换做成可复用的校验闭环脚本能跑只是及格能验证才算落地。我一般会在转换后加一步校验统计源文件数、输出文件数、每个 csv 的行数和源表行数做比对。下面这段是校验逻辑跑完输出一份对账表。def verify(src_dir: str, dst_dir: str): src_files list(iter_excel_files(Path(src_dir))) dst_files list(Path(dst_dir).rglob(*.csv)) report [] for p in src_files: df read_one(p) # 源表行数不含表头 src_rows len(df) # 对应输出文件按命名规则找 out Path(dst_dir) / p.relative_to(src_dir).with_suffix(.csv) dst_rows sum(1 for _ in out.open(encodingutf-8-sig)) - 1 if out.exists() else -1 report.append((p.name, src_rows, dst_rows, src_rows dst_rows)) return report逻辑说明src_rows dst_rows是核心断言行数对不上说明转换过程中丢了数据优先查合并单元格和空行。参数上-1是去掉表头行。这个校验跑一遍比肉眼翻文件可靠得多。再补一个习惯把输入目录、输出目录、编码、sheet 策略写进一个配置文件脚本读配置执行。这样换一批文件只改配置不用动代码。我踩过最深的坑是某次直接改了脚本里的路径结果把上一批的输出覆盖了从那以后每次转换前都强制走一遍“配置 校验”两步确认输出目录是新的才开跑。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑