简介:在数据分析与大数据处理场景中,CSV文件动辄超过百万行,传统编辑器很难直接打开。这款CSV大文件分割工具正是为解决此类问题而生,面向数据分析师、ETL开发人员及Hadoop生态使用者,支持按行数、文件大小或特定列值灵活切分,便于后续导入、传输或并行计算。
资源包仅533KB,共4个文件,以Windows可执行程序为主,附带txt说明、htm帮助页面和url快捷链接,解压即可独立运行,无需复杂环境配置。工具轻量小巧,适合作为Hadoop预处理链路的辅助组件。
目前已有5570人浏览学习。借助它,可将千万行级CSV快速拆分为多个小文件,有效规避内存溢出与卡顿,明显提升数据清洗和分析效率,尤其适合经常处理大型表格的初、中级数据工作者。使用过程中还可参考内置帮助文档,快速掌握按行数或大小拆分、批量输出等操作,少走弯路。
1. CSV大文件分割工具:Excel崩溃、ArcGIS报错之前先切一刀
处理csv大文件的人大概率都经历过这种场景:手头一个300MB的csv,Excel双击直接卡死,ArcGIS导入时提示字段类型错误,Hadoop那边又要求数据按块分好才能喂给Hive分区表。问题不在数据本身,而在工具默认把整个文件一次性装进内存。csv大文件分割工具干的就一件事:把大csv按行切成多份小文件,让Excel能打开、ArcGIS能导入、Hadoop能直接消费。适合做数据分析、GIS数据处理、ETL入仓的从业者,导出的分片既能合并回去,也能独立使用。下面按实际使用路径拆开讲:分割粒度怎么选、脚本怎么写、参数怎么调、坑在哪。
2. 分割粒度怎么选:按行数是底线,按字节切是翻车现场
2.1 先搞清楚为什么很多分割工具会切出残缺行
拿到的分割工具有些默认是"按文件大小MB"去切。对二进制文件比如压缩包、日志快照,按字节切没问题;对csv这种行式文本,按字节切就是个雷。csv的每一行由换行符结尾,按字节数硬切,你根本保证不了切点正好落在换行符后面,结果就是某个数据行被从中间劈开,生成半行残记录。这种分片导入Excel会直接报"文件格式不对",导入ArcGIS属性表也会出现一条撕裂的空白记录,后面清洗起来比重新下载还痛苦。
我一般只按行数切。行是csv里最小的完整数据单元,每一行都有完整的字段序列,按行切出来的文件一定是合法csv,合并回去也和原文件一模一样。如果某个工具宣称"按大小切",先确认它是不是先按行缓冲、累计到接近目标大小再落盘,纯字节切的那种趁早别用。这里有个选型表格:
| 分割模式 | 结果是否保证合法 | 适用场景 |
|---|---|---|
| 按行数切(每份固定N行) | 保证,行是完整单元 | 所有场景,推荐 |
| 按字节大小切(行缓冲后落盘) | 保证,近似大小 | 想让分片体积接近可接受 |
| 纯字节切割 | 不保证,可能劈开行 | 仅限非行式文本,别用 |
2.2 行数上限怎么定:先采样统计,再对齐下游工具
定了按行切,下一问是"每份切多少行"。这个数不能拍脑袋,我习惯先拿原始文件做两个统计:总行数和平均行长。Linux下直接wc -l拿总行数,再用ls -l拿文件字节数,两者一比就是平均行长。假设一个csv是500MB、200万行,平均行长260字节,你希望单份文件控制在50MB以内,那单份行数就是50 * 1024 * 1024 / 260,约20万行。这只是理论值,还要对齐下游工具的承受能力:
- Excel单表上限是1048576行,但超过三四十万行时公式重算已经明显卡顿,所以单份控制在20万到50万行比较舒服。
- ArcGIS导入csv时不卡在行数上,卡在字段类型识别上,尤其数值列前几行混入字符串,就会整体识别成文本。单份行数多点少点影响不大,但要保证每份的字段结构完全一致。
- 如果最终要落到Hadoop的Hive表,建议对齐HDFS块大小思路,单份文件控制在128MB附近或整数倍,以平均行长倒推行数。这样分片在HDFS上分布均匀,MapReduce处理时不会有某个mapper被大文件拖死。
实际动作是先用命令行做一轮统计,再决定切分参数。常见做法是先跑wc -l和head -n 20看表头结构,确定表头占几行,再决定行数参数。这一步不花两分钟,但能避免把200万行切成2000多个小文件——那会让下游合并比不切还麻烦。
2.3 表头单独抽出来,是分割流程里最容易漏的一步
分割工具还会遇到一个隐藏问题:表头怎么办。多数csv默认第一行是列名,有的工具图省事"每份都保留表头",这就埋了雷。分片如果每份都带表头,后续用cat part_*.csv合并回去,表头混进数据行,列数对不上,Hive加载时会跳错。反过来,如果只在第一片留表头,后面的分片想单独导入ArcGIS或Excel,又得手动补列名。更复杂的情况是表头本身不是一行,比如从football-data.co.uk下载的足球赔率文件就有两行表头,第一行列名、第二行单位说明。这类文件分割时就得支持"跳过前N行"而不是写死第一行。
我自己的习惯是:先把表头行读出来缓存到变量里,每写一个分片,按场景决定是否追加表头。默认行为是每份都保留表头,这样每个分片自洽,单独拿给谁都少废话;如果最终要合并或者导Hive,用tail -n +2或脚本去掉每份的第一行即可。工具参数里至少要暴露header_rows,让使用者告诉它表头占几行,而不是默认猜一行。
3. Python实现分割脚本:从标准库到命令行参数完整落地
3.1 用流式读取而不是read_csv,避免2GB文件把内存打爆
很多人习惯用pandas的read_csv把整个文件读成DataFrame,再分片to_csv。这个写法在小文件上没毛病,大文件直接内存翻车:2GB的csv,pandas为了做类型推断通常要吃3到4倍内存,32GB的机器都给你吃满然后疯狂swap。正确做法是流式读取,用Python标准库csv.reader逐行迭代,累计够指定行数就写一个新文件。这样内存里始终只有一个batch列表,20万行的batch最多也就占几十MB,跟文件总大小无关。
这里给出一个可以直接复制运行的分割脚本,没有第三方依赖,Python 3.6以上就能跑:
import csv import argparse import os def write_batch(output_dir, file_no, header, batch): """把一批行写入一个分片文件,按顺序附上表头""" path = os.path.join(output_dir, f"part_{file_no:04d}.csv") with open(path, 'w', encoding='utf-8', newline='') as f: writer = csv.writer(f) for h in header: writer.writerow(h) writer.writerows(batch) print(f"已生成 {path}, 行数 {len(batch)}") def split_csv(input_path, output_dir, rows_per_file=200000, header_rows=1): os.makedirs(output_dir, exist_ok=True) header = [] file_no = 1 batch = [] with open(input_path, 'r', encoding='utf-8', newline='') as f: reader = csv.reader(f) for _ in range(header_rows): try: header.append(next(reader)) except StopIteration: return for row in reader: batch.append(row) if len(batch) >= rows_per_file: write_batch(output_dir, file_no, header, batch) file_no += 1 batch = [] if batch: write_batch(output_dir, file_no, header, batch) if __name__ == '__main__': parser = argparse.ArgumentParser(description='csv大文件分割工具') parser.add_argument('--input', required=True, help='输入csv路径') parser.add_argument('--output', default='./split_out', help='输出目录') parser.add_argument('--rows', type=int, default=200000, help='每个分片行数') parser.add_argument('--header-rows', type=int, default=1, help='表头占几行') args = parser.parse_args() split_csv(args.input, args.output, args.rows, args.header_rows)几个关键点说明一下。newline=''必须带上,否则Windows环境会把\r\n自动改写成\r\r\n,切出来的分片用文本编辑器看没问题,一导入ArcGIS就多出空行。csv.reader和csv.writer自带引号处理,行里如果字段包含逗号或换行符,读出来是一个完整字段,写回去也会自动加引号,不会出现一列被拆成两列的情况。batch列表的容量就是你单片的行数,默认20万,最多保存内存里几十MB的对象引用,内存安全。rows_per_file和header_rows都暴露成参数,适配不同格式的表头。
3.2 命令行参数与命名规则:对齐Hadoop分片习惯
脚本提供四个参数,实际使用中只需要关心--rows和--header-rows。--rows按第2章的估算公式来定,一般取整数方便记忆。--header-rows前面说过,单行表头填1,双行表头填2。--input和--output是常规路径参数,注意输出目录如果存在同名文件不会覆盖,因为文件名带了4位序号,从part_0001.csv开始递增。
为什么命名从0001开始而不从0000?因为很多Hadoop导入工具和shell脚本用part_*通配符取文件,如果从0000开始,某些排序算法会把part_0000.csv和part_00000.csv混在一起,后续合并顺序错乱。用固定4位宽度,排序就是字典序加数字序双保险。跑完一轮实际命令长这样:
python split_csv.py --input ./data/raw_2024.csv --output ./data/split --rows 200000 --header-rows 1跑之前先wc -l ./data/raw_2024.csv确认总行数。跑完之后用ls ./data/split | wc -l数一下分片个数,再自己心算验证:(总行数 - header_rows) / 200000 + 1就是预期分片数,对不上就说明原文件末尾有空行或者行被读取异常。这一步多花十秒钟,能挡住一半的分割事故。
3.3 处理GB级文件时的性能预期与瓶颈
流式读写的性能瓶颈基本在磁盘IO和编码解码上。CSV文件如果很大,Python标准库逐行迭代的速度大概在每秒20万到40万行,200万行文件大约十秒切完,速度可以接受。如果你发现速度远低于这个水平,先检查是不是打开了文件校验软件(杀毒软件实时扫描),把输出目录加入白名单会有明显改观。另一个影响性能的是encoding参数,UTF-8解析比GBK快,如果源文件是GBK编码且没有特殊字符要求,可以先用工具转成UTF-8再分割,后面导入Hadoop也更顺。
4. 表头与编码修复:分割结果导入Excel和ArcGIS不乱码的关键
4.1 每份保留表头还是只在首份保留:分场景选,别一刀切
第2章提过表头策略,这里展开说。工具默认每份分片都带表头,这个选择背后有实际考量:假设你把分片交给另一个负责做GIS配图的人,他手里拿到的每一个csv都应该有列名,否则不知道哪个字段是坐标、哪个字段是属性。但如果你的目的是把分片上传到Hive,每份表头就是累赘,Hive读取时会当成一条脏数据。我建议工具同时提供--keep-header开关,默认保留,需要处理Hadoop时用脚本统一去掉。这里少折腾的办法是:分片阶段保留表头,入仓前用一条awk命令批量去掉:
for f in ./data/split/part_*.csv; do tail -n +2 "$f" > "${f%.csv}_nohead.csv" done这个循环从每个分片去掉第一行,生成_nohead后缀的新文件。注意如果表头是两行,tail -n +2要改成tail -n +3。去掉表头后的文件合并不再需要额外过滤,cat顺序拼起来就是一份干净的全量数据。
4.2 编码统一:UTF-8无BOM给Hadoop,UTF-8带BOM给Excel和ArcGIS
编码坑是我在实际项目里遇到最多次的翻车点。同一份csv,用Excel双击打开中文完全正常,用ArcGIS导入就乱码,用cat看一眼规则,可能是UTF-8带BOM。BOM是文件开头的三个不可见字节EF BB BF,Excel读取时靠它识别UTF-8编码,没有BOM会默认按ANSI(GBK)解,于是中文全变问号。而Hadoop生态不认BOM,hdfs dfs -put上去之后Hive导数据会报"字段开头非法字符",本质上就是BOM粘到了第一列列名上。
所以正确逻辑是:分片给人用Excel/ArcGIS看,写UTF-8带BOM;分片给Hadoop消费,写UTF-8无BOM。第3章的脚本写的是无BOM,如果想带BOM,打开文件时加一行utf-8-sig编码:
with open(path, 'w', encoding='utf-8-sig', newline='') as f:判断源文件当前编码,可以用chardet库做检测,也可以在Linux下用file -bi命令:
file -bi ./data/raw_2024.csv返回charset=utf-8就是无BOM的UTF-8,返回charset=iso-8859-1或者gb2312就要考虑转换。常见做法是统一转成UTF-8,因为后面接ArcGIS也好、接Hadoop也好,UTF-8都是最通用的中间格式。转换用iconv:
iconv -f GBK -t UTF-8 ./data/raw_2024.csv > ./data/raw_2024_utf8.csv4.3 分片之后如何快速验证能被下游直接识别
分割完成不等于分割成功。我一般连跑三个验证动作:第一个是wc -l校验每个分片行数,看看最后一个文件是不是只有几百行——如果最后一个文件行数远小于设定值,说明原始文件可能有大量空行或结尾没有换行符;第二个是检查第一个分片字段数是否一致,用awk打印每行列数,如果有某行列数少于表头列,说明数据里的换行符处理出了问题;第三个是直接抽第一个分片,用Excel或ArcGIS试开一下。这三个动作里,第二个最容易被忽略,但有价值——它可以在数据进入下游前拦截掉最隐蔽的字段错位问题。
awk -F',' '{print NF}' ./data/split/part_0001.csv | sort | uniq -c这条命令统计第一片每行的字段数。如果输出除了一个主要数字只有一个次要数字,说明基本是干净的;如果出现两三个不同数字,那大概率有引号跨行问题,要回源头排查原始文件是不是某些字段里包含了换行符。注意这个检查只适用于没有引号包裹的简单csv,带引号的csv字段里允许有逗号,awk按逗号切就会误判,更严谨的检查得用Python的csv模块读一遍。
5. 常见问题排查:行数对不上、字段错位、乱码的5条踩坑记录
5.1 分割后总行数变少了
现象:把分片行数加起来,比原始文件wc -l的行数少了上千行。
原因:原始文件最后一行没有换行符。csv读取器在迭代到最后一行时能读到内容,但部分脚本如果按"\n"切行,最后一行可能被丢弃。另外,如果文件里存在以\r结尾的行(旧Mac格式),在Linux下wc -l也不认,导致统计口径不一致。
解决:分割前用dos2unix或sed -i 's/\r$//'统一换行符格式。更稳妥的是让脚本基于csv.reader的迭代结果来计行,而不是依赖外部wc -l的统计。分割后再用Python读一遍分片并数行,以脚本统计为准。
5.2 导入Excel中文全部变成乱码
现象:分片文件用cat看内容正常,用Excel打开中文是"锟斤拷"。
原因:脚本写文件时用了utf-8(无BOM)编码。Excel在Windows中文环境下默认按ANSI/GBK去解UTF-8文件,没有BOM就把它当成GBK解,中文字节被拆开就成了乱码。
解决:把输出文件的编码改成utf-8-sig,写入时会自动加BOM头。如果已经生成了一堆无BOM分片,用sed -i '1s/^/\xef\xbb\xbf/'给每个文件开头插入BOM,或重跑一次分割脚本并在打开输出文件时用utf-8-sig。
5.3 导入后字段错位,一列的数据跑到了下一列
现象:分片能打开,但某几行字段数异常,比如本应20列变成19列,数据整体向右错一格。
原因:原始文件有些字段内含逗号但没有用引号包裹。csv格式约定了这种场景必须加引号,而手工导出的数据常违规。Excel读取时看到裸逗号就切成新列,字段往后串位。分割工具只负责按行切分,不会修数据内部的问题。
解决:先检查原始文件里违规行的样例,用文本搜索定位。如果整份文件只有少量问题,在分割前做一次修复,把「逗号后面紧跟数字且整行没有引号包裹」的列用引号包起来,这步只能用编程方式处理:读一行,按已知表头字段数倒推哪几个位置是数据列,对含逗号字段手动补引号。多数情况下,更现实的做法是回到导出方修复规则。
5.4 内存还是爆掉了
现象:跑了脚本,Python进程内存持续上涨,最后OOM被系统杀掉。
原因:虽然用流式读取,但batch列表里存的是每行的字段列表,如果一行有几百上千个字段,20万行的batch可能占用1GB以上。另一种情况是源文件里有一个超大字段(比如一列塞了整个HTML源码),那一行本身就有几十MB。
解决:把--rows调小,比如从200000降到50000。对于单行超大字段的情况,需要限制每行的字段长度,或者用csv.field_size_limit()加大限制。实际上更狠的办法是改成"写入行数超过阈值或者batch内存估算超过阈值就落盘",但在脚本工具阶段调小行数就够了。
5.5 分片文件大小严重不均,最后一个文件特别大
现象:设定每份20万行,前几份都是50MB多,最后一份却有200MB。
原因:数据分布不均匀。某些区域的记录里包含长文本字段,比如评论、地址、备注,这些行的字节数远超平均行长,导致同样行数下文件体积差异巨大。
解决:如果下游需要均衡大小(比如HDFS上希望文件接近块大小),改用混合策略:先按行数切,再用脚本统计每份实际字节数,把大小超标的文件重新二等分;或者直接以"累计字节数达到阈值就落盘"为切分条件,同时保证行完整。我一般做法是设置双条件,行数和字节两个阈值谁先到就触发落盘,兼顾公平与格式安全。
6. 进阶用法:分割后再校验三件事,把结果安全交给Hadoop
6.1 分割完成后的自检流程
花两分钟做自检,比事后在Hive里发现缺数再回头找源头划算得多。我的固定流程三步走:第一步用md5sum对比分片合并后与原始文件的校验和,但这要求合并时严格按顺序且不混入表头;第二步用wc -l累加所有分片行数,加上表头行数等于原始文件总行数;第三步抽检一个包含中文和逗号的字段,确认不出现列错位。这三件事在图形界面工具里是做不了的,只能用命令行,我一般写成一段两行脚本快速执行。
6.2 对接Hadoop时避免小文件过多
直接把几万个分片交给HDFS不是明智选择,每个分片默认是一个块,几万个块意味着NameNode内存压力和中途任务调度开销剧增。推荐做法是分割后先合并到接近128MB的文件再上传:如果原始文件是500MB,理想分片是4个128MB,而不是20个25MB。所以一开始定--rows时就要按第2章的公式对齐HDFS块大小,而不是随手填个10万行。我习惯先跑wc -l和du -h得到平均行长,再倒推--rows,确保生成的文件数量在个位数到两位数之间。
6.3 在Hive建表前用一条命令快速验证分片字段
正式LOAD DATA之前,先在默认数据库里建一张临时表,只读第一个分片,用select * from tmp limit 5看字段映射是否正常。这一步能拦截掉百分之八十的"列错位、类型错配"问题。从那以后,我每次分割完都强制走一遍校验流程,哪怕是在别人机器上临时跑工具也绝不跳过。希望帮到你。
本文还有配套的精品资源,点击获取