
简介这是一款面向数据分析师、大数据工程师及IT从业者的CSV大文件分割工具专门解决超过百万行的CSV文件难以用普通编辑器或表格软件打开、处理的问题。工具支持按行数、文件大小或指定列值等条件将大文件快速拆分为多个小文件便于后续导入、备份、传输与分析并可配合Hadoop生态下的MapReduce、Hive等组件完成数据预处理。资源包共4个文件包含exe可执行程序、htm说明页面、url快捷链接及txt使用说明压缩包约533KB解压后即可运行主程序输入文件路径并设置分割参数即可自动完成拆分。目前已有5571人学习下载适合需要处理大规模CSV数据、优化数据导入流程的读者参考使用可帮助快速掌握大文件分割思路提升数据预处理效率。1. 百万行 CSV 打不开问题不在 Excel 而在分割策略上周同事丢来一个 1.2GB 的 CSV说双击后 Excel 转圈十分钟最后只显示前 104 万行剩下的数据像被吞了。这不是 Excel 的 bug是它单表行数上限决定的硬边界。做数据分析、跑 football-data.co.uk 这类公开赛程数据、或者从业务库导出的宽表动辄几百万行直接打开必然翻车。所谓 csv 大文件分割工具核心就一件事把一个大 CSV 按行数、按体积或按字段值切成若干个小文件让每个文件都能被 Excel、pandas、数据库导入工具正常吃下。它适合三类人天天和导出报表打交道的运营分析、要把 csv 导入数据库的工程师、以及拿 csv 做建模前处理的数据同学。下面我按自己踩过的坑把选型、参数、代码和排查一次讲透。2. 先想清楚切法按行、按体积还是按字段2.1 三种切分维度的适用边界按行数切是最常见的做法比如每 50 万行一个文件。它的好处是结果可预测坏处是如果单行特别宽几百列50 万行可能还是几百 MBExcel 照样打不开。按体积切更贴近“能不能打开”这个真实诉求常见做法是设定目标大小比如 80MB边写边累计字节数超了就换文件。缺点是同一批数据切出来的文件行数不一致做后续并行处理时任务不均衡。按字段值切比如按日期、按地区本质是分组导出适合后续要按维度分别分析的场景但它不是通用分割得先知道业务字段。我一般会先问一句切完是给谁用给人看就按体积给程序跑就按行数给分库分表就按字段。选错维度后面全是返工。2.2 流式读取为什么是唯一正确姿势很多人第一反应是pandas.read_csv()读进来再to_csv()切。1GB 以内勉强能跑再大就吃满内存机器直接卡死。正确做法是流式逐行读、逐行写内存占用只和单行大小有关和文件总大小无关。Python 标准库的csv模块配合文件迭代就是最稳的方案不依赖 pandas也不会有 dtype 推断带来的意外。import csv import os def split_by_rows(src_path, out_dir, rows_per_file500000): os.makedirs(out_dir, exist_okTrue) base os.path.splitext(os.path.basename(src_path))[0] # newline 是 csv 模块的硬性要求否则 Windows 下会多出空行 with open(src_path, r, encodingutf-8-sig, newline) as fin: reader csv.reader(fin) header next(reader) # 先取出表头每个分片都要带上 file_idx 1 row_count 0 fout None writer None for row in reader: if row_count % rows_per_file 0: if fout: fout.close() out_path os.path.join(out_dir, f{base}_part{file_idx}.csv) fout open(out_path, w, encodingutf-8-sig, newline) writer csv.writer(fout) writer.writerow(header) # 每个分片重复写表头 file_idx 1 writer.writerow(row) row_count 1 if fout: fout.close() return file_idx - 1 if __name__ __main__: n split_by_rows(big.csv, ./output, rows_per_file500000) print(f生成 {n} 个分片)逻辑说明encodingutf-8-sig是为了兼容 Excel 打开时中文不乱码BOM 头会被正确识别。newline不加的话在 Windows 上每行后面会多一个空行这是 csv 模块文档里明确写的。rows_per_file控制每个文件的数据行数不含表头。判断换文件的时机是row_count % rows_per_file 0注意这个判断在写当前行之前所以第一个文件从第 0 行开始就新建。表头在每个分片都重写一遍否则单独打开某个分片会没有列名。2.3 按体积切分的字节累计写法按体积切要处理一个细节不能按字符数算得按实际写入的字节数算否则中文和英文混排时估算会偏。做法是每次写完一行用fout.tell()拿当前文件偏移量超过阈值就换文件。import csv, os def split_by_size(src_path, out_dir, max_bytes80*1024*1024): os.makedirs(out_dir, exist_okTrue) base os.path.splitext(os.path.basename(src_path))[0] with open(src_path, r, encodingutf-8-sig, newline) as fin: reader csv.reader(fin) header next(reader) file_idx, fout, writer 1, None, None for row in reader: if fout is None or fout.tell() max_bytes: if fout: fout.close() out_path os.path.join(out_dir, f{base}_part{file_idx}.csv) fout open(out_path, w, encodingutf-8-sig, newline) writer csv.writer(fout) writer.writerow(header) file_idx 1 writer.writerow(row) if fout: fout.close() return file_idx - 1max_bytes设成 80MB 是个经验值Excel 打开 80MB 的 CSV 大概十几秒再大体验就差了。fout.tell()返回的是当前文件指针位置也就是已写入的字节数注意它统计的是编码后的字节不是字符数所以对中文友好。判断放在写行之前保证不会出现某个文件刚超一点就立刻再切一个空文件的情况。3. 命令行一把梭不写代码也能切3.1 Linux/macOS 下用 split 快速按行切如果只是临时切一下不想写脚本系统自带的split就够用。它按行切速度极快底层是 C 实现。# -l 每个文件行数-d 用数字后缀--additional-suffix 加扩展名 # 注意split 会把表头也当成普通行只有第一个分片有表头 split -l 500000 -d --additional-suffix.csv big.csv part_这条命令的坑在于表头。split不认识 CSV 结构它把第一行当数据切走结果只有part_00.csv有表头后面所有分片都没有列名。解决办法是先抽出表头切完再给每个分片补上。head -n 1 big.csv header.csv tail -n 2 big.csv | split -l 500000 -d --additional-suffix.csv - part_ for f in part_*.csv; do cat header.csv $f tmp mv tmp $f donetail -n 2表示从第二行开始取跳过表头。split的输入用-表示从标准输入读。补表头那步用临时文件再 mv避免直接覆盖导致数据丢失。这套组合在几十 GB 的文件上也能跑因为全程流式。3.2 Windows 下的等价方案Windows 没有原生split可以用 PowerShell 或者装个 Git Bash。PowerShell 写法可读性差且慢我更推荐直接用 Python 脚本跨平台一致。如果非要用命令行Git Bash 里上面的split命令可以直接用。另一个选择是csvkit里的csvsplit但它要额外装 Python 环境本质还是回到 Python 方案。提示不管用哪种命令行方案切完一定要抽查首尾两个分片确认表头在、行数对、没有截断的半行。4. 避坑与排查切完打不开、乱码、少行怎么办4.1 现象切完 Excel 打开中文全是乱码原因源文件是 GBK 编码脚本按 UTF-8 读或者反过来。CSV 本身不带编码声明全靠约定。解决先用file -i big.csv看编码或者用 Python 的chardet探测。读的时候用探测到的编码写的时候统一用utf-8-sig这样 Excel 能正确识别中文。如果源是 GBK把open的encoding改成gbk即可输出仍建议utf-8-sig。4.2 现象分片行数加起来比原文件少原因源文件里存在字段内换行也就是某个单元格内容本身带\n被引号包裹。csv.reader能正确处理这种跨行记录但如果你用for line in fin逐行读就会切错。解决坚持用csv.reader不要自己按\n切。另一个可能是最后一行没有换行符某些工具会漏掉Python 的 csv 模块不会。排查方法是用wc -l对比原始行数和分片行数之和差值就是跨行记录数。4.3 现象切出来的文件大小极不均匀原因按行切时某些行特别长比如一列存了长文本或 JSON导致某个分片体积暴涨。解决改用按体积切或者按行切后检查最大分片体积必要时对超大分片二次切分。我一般会在切完后跑一句ls -lh output/ | sort -k5 -h看体积分布超过预期两倍的就单独处理。4.4 现象内存还是爆了原因虽然用了流式读但某处不小心把结果收集进了列表比如rows list(reader)。解决全局搜索代码里有没有list(、readlines()、read()这类一次性读取的调用。流式方案里除了当前行和表头不应该有任何累积结构。另外csv.field_size_limit默认有上限遇到超长字段会抛field larger than field limit需要手动调大。import csv, sys csv.field_size_limit(sys.maxsize) # 解除单字段长度限制4.5 现象切完导入数据库报主键冲突原因按行切时如果源数据本身有重复主键或者切分导致同一主键的记录分散在不同分片导入时可能触发唯一约束。解决切分前先确认业务上是否允许跨分片如果主键必须聚在一起就得按主键排序后再切或者按主键哈希分片。这不是分割工具的问题是切分维度选错了。5. 进阶切完顺手校验别让脏数据流到下游切分本身不难难的是切完你敢不敢直接用。我的习惯是切完立刻做三件事行数校验、表头校验、抽样比对。行数校验用wc -l对每个分片求和和原文件比差值应该等于跨行记录数乘以分片数减一。表头校验是读每个分片第一行确认和原表头完全一致。抽样比对是随机抽几行确认字段没串位。更进一步可以把校验写进脚本切完自动输出一份报告。import csv, os def verify(src_path, out_dir): with open(src_path, r, encodingutf-8-sig, newline) as f: src_rows sum(1 for _ in csv.reader(f)) - 1 # 减去表头 total 0 for name in sorted(os.listdir(out_dir)): if not name.endswith(.csv): continue with open(os.path.join(out_dir, name), r, encodingutf-8-sig, newline) as f: rows sum(1 for _ in csv.reader(f)) - 1 total rows print(f{name}: {rows} 行) print(f源文件 {src_rows} 行分片合计 {total} 行差值 {src_rows - total})这个校验脚本本身也是流式的不会吃内存。差值不为零时优先怀疑字段内换行和编码问题。如果差值等于分片数减一那基本就是表头被重复计数了检查一下每个分片是不是都带了表头。参数上我一般把rows_per_file设在 30 万到 50 万之间max_bytes设在 80MB 左右这两个值是根据 Excel 的打开体验和 pandas 的读取速度折中出来的。如果你的下游是数据库LOAD DATA行数可以放大到 100 万因为数据库不在乎单文件大小只在乎导入批次。切分维度、编码、表头这三样确认清楚剩下的就是跑脚本等结果。我踩过最深的坑是拿 GBK 文件按 UTF-8 切切完中文全乱回头查了半天以为是分割逻辑错了其实是编码没对齐。希望帮到你。本文还有配套的精品资源点击获取