简介:针对逗号分隔值(CSV)文件因体积过大而无法直接导入MATLAB、Excel等软件的问题,这款csv拆分工具提供了实用的轻型解决方案,尤其适合数据分析、开发及测试人员按需将大文件切分为多个小文件,从而降低单次导入时的内存压力,避免数据读取中断。资源包为zip压缩格式,体积仅10KB,一共包含3个文件:绿色免费的主程序(exe)、辅助上手的说明文档(htm)以及一个来源快捷方式(url),解压后双击即可运行,无需安装、不修改系统注册表,放在U盘里就能随身携带使用。CSV文件过大往往源于记录条数高达数万甚至更多,或单条记录的字段内容过多。工具支持按行数或目标大小自定义拆分条件,同时可指定输出目录,一次性批量完成分割。拆分后的小文件既可循环调用MATLAB的readtable函数读取,也适用于Excel、Python等其他数据分析环境,有效提升大数据集的处理稳定性。随附的说明文档对使用流程和注意事项做了清楚交代,上手门槛低,适合需要频繁处理超大CSV数据的工程师和学生使用。目前已有1136人学习下载,对于日常数据处理场景而言,是一款值得收藏的轻量小工具。 我去年年底遇到一个头疼事:营销部门导出一份年度订单明细,一个 CSV 文件就有 2 个多 G,Excel 直接打不开,连用记事本打开都要卡半天。更麻烦的是,业务同事说这份数据要按月份拆成 12 份,分别发给不同区域的负责人跟进。我当时脑子里第一个念头就是,写个脚本半小时搞定,但转念一想,团队里懂 Python 的就我一个人,后面再有类似需求总不能每次都找我。于是我花了一个晚上,把“CSV 拆分”这件事从需求到实现完整梳理了一遍,做成一个简单好用的工具,今天把这些经验整理出来,希望能帮你少走弯路。
无论你是数据分析师、运维工程师,还是经常跟表格打交道的运营同学,只要工作中会碰到“文件太大打不开”“需要按某个规则把数据切开”的场景,这篇文章都适合你。我会从需求分析、工具选型,到代码实现和常见坑位,完整走一遍流程,保证你读完能直接照着做。
1. 为什么需要拆分 CSV 文件
1.1 大型 CSV 的常见痛点
CSV 这种格式可以说是数据交换的“通用语言”,几乎任何数据库、数据分析工具、办公软件都能识别它。但它有个天生的短板:没有任何索引和压缩机制,纯粹靠明文存储。当数据量到了几十万行、几百万行的时候,文件体积会膨胀到几百 MB 甚至几个 G,随之而来的就是一连串日常问题。
最典型的就是 Excel 打不开。Excel 单张工作表的上限是 1048576 行,超过这个行数就无法完整展示;即便行数没超,几百 MB 的 CSV 文件也会让 Excel 卡死在加载界面。在你点开文件的那几十秒里,风扇狂转、鼠标转圈,最后还可能直接弹出“未响应”。还有一类场景是数据入库,比如把 CSV 导入 SQL Server 或者 MySQL,文件太大时导入过程会非常耗时,而且一旦中间遇到格式问题,整次导入失败,排查错误位置极其痛苦。再比如日志文件,我们常用 CSV 格式存服务访问日志,单天日志动辄几个 G,做检索和归档都极为不便。
1.2 拆完之后能解决什么问题
把一个大文件拆成多个小文件,解决的不只是“能打开”的问题。对数据分析师来说,拆分后的数据可以并行处理,比如分成 8 份,开 8 个进程同时跑统计,效率能翻好几倍。对业务人员来说,分区域、分时间段的数据可以直接分发给对应负责人,不用每次手工筛选。对运维岗位来说,大文件归档前先拆分,可以显著降低存储和传输的压力。
拆分的维度主要有三种:按行数拆分,比如每 5 万行一个文件;按列拆分,比如把包含 50 列的大宽表按字段类别拆成两个文件;按内容分组拆分,比如按“月份”“地区”“用户ID”等某个字段的值来划分数据。实际工作中,按内容分组是最常见的需求,但实现方式也最容易被新手搞出乱子。
2. 工具选型解析:拆分方案对比
2.1 三种主流方案
针对 CSV 拆分,市面上能用的方案五花八门。我在实操中主要把方案分成三类,各有适用场景。
第一类是直接用 Excel 操作。用 Excel 打开文件后,按条件筛选、复制、粘贴到新文件。优点是不用学任何技术,但缺点非常致命:大文件根本打不开,而且人工操作时如果筛选条件没选对,数据残漏了都不容易发现。这种方法只适合几千行的小文件。
第二类是命令行工具,比如 Linux 下的 split 命令。它的速度极快,几秒钟就能切开几个 G 的文件,但它有个硬伤:它按字节或行数硬砍,不识别 CSV 的列结构。如果你要按某个字段的值分组拆分,或者拆分时希望保留表头,split 命令做起来就很不顺手,需要额外拼接。
第三类是我最推荐的:写一个专用 Python 脚本。Python 生态里 pandas、csv 模块都能处理这类需求,代码量不大,却能灵活控制拆分逻辑。你既能按行数拆,也能按列拆,还能按字段值分组拆,甚至可以在拆分的过程中顺手做数据清洗。虽然有一点点学习门槛,但这套脚本可以反复复用,一次投入,长期受益。
2.2 为什么我选择 Python 脚本
我自己在实际处理中,90% 的场景都用 Python 脚本完成。原因有三个。第一,可定制化程度高,我可以把“按月拆分”“按地区拆分”“顺便过滤掉金额为空的记录”这些业务规则一次性写进脚本里,下次直接套用。第二,错误可控,脚本会明确提示每个文件的行数、列数、是否缺失表头,如果某次拆分结果和预期不一致,我能立刻追溯。第三,跨平台,我的电脑是 Windows,服务器是 Linux,同一套脚本两边都能跑。
很多完全不碰代码的同学会担心 Python 环境搭建麻烦,其实现在的 Anaconda 或者 Python 官方安装包都是图形化安装,一路点“下一步”,十分钟就能装好。后面我会给出完整的脚本,你不用理解每一行代码的含义,改好路径和参数就能用。
3. 核心功能设计与代码实现
3.1 设计目标与核心逻辑
先说清楚这个工具到底要做什么,梳理清楚需求再写代码,事半功倍。我的目标拆分成三个具体功能点:
一,支持按行数拆分,设定每个文件包含多少行数据,自动把大文件切开,每个子文件都保留表头。二,支持按指定字段的取值拆分,比如以“月份”列为基准,把 1 月到 12 月的数据分别输出到不同的 CSV 文件。三,输出拆分结果汇总信息,比如总共多少行、生成多少个文件、每个文件的行数,方便核对。
核心逻辑其实不复杂。读一个大文件时不能一次性把所有数据 load 进内存,因为那样内存会爆掉。正确做法是逐行扫描,用 csv.reader 读取每一行,再根据拆分条件决定这一行应该写入哪个输出文件。按行数拆分时,写满一个文件就关闭并打开下一个文件;按字段值分组时,使用一个字典来维护“字段值 → 文件句柄”的映射关系。
3.2 完整的拆分脚本实现
下面给出一个通用性很强的实现,我平时用的核心脚本就长这样,你复制后稍微改改就能直接用。
import csv import os import sys from collections import defaultdict def split_by_row_count(input_file, output_dir, chunk_size=50000, has_header=True): """ 按行数拆分CSV文件 :param input_file: 输入CSV路径 :param output_dir: 输出目录 :param chunk_size: 每个文件包含的数据行数(不含表头) :param has_header: 原文件是否有表头 """ os.makedirs(output_dir, exist_ok=True) file_index = 1 current_rows = 0 with open(input_file, 'r', encoding='utf-8-sig', newline='', errors='replace') as f: reader = csv.reader(f) header = next(reader) if has_header else None output_path = os.path.join(output_dir, f"split_{file_index:03d}.csv") out_f = open(output_path, 'w', encoding='utf-8-sig', newline='') writer = csv.writer(out_f) if header: writer.writerow(header) for row in reader: writer.writerow(row) current_rows += 1 if current_rows >= chunk_size: out_f.close() file_index += 1 current_rows = 0 output_path = os.path.join(output_dir, f"split_{file_index:03d}.csv") out_f = open(output_path, 'w', encoding='utf-8-sig', newline='') writer = csv.writer(out_f) if header: writer.writerow(header) out_f.close() print(f"拆分完成,共生成 {file_index} 个文件,每个文件最多 {chunk_size} 行数据")再来看按字段值拆分的实现。这个稍微复杂一点,核心是维护一个字典,key 是字段的取值,value 是对应的文件句柄和 writer。
def split_by_column_value(input_file, output_dir, column_index=0, has_header=True): """ 按指定列的值拆分CSV文件 :param input_file: 输入CSV路径 :param output_dir: 输出目录 :param column_index: 作为拆分依据的列索引(从0开始) :param has_header: 原文件是否有表头 """ os.makedirs(output_dir, exist_ok=True) file_map = {} # 字段值 -> (文件句柄, writer) header_written = set() with open(input_file, 'r', encoding='utf-8-sig', newline='', errors='replace') as f: reader = csv.reader(f) header = next(reader) if has_header else None if header and (column_index < 0 or column_index >= len(header)): print(f"警告:列索引 {column_index} 超出表头范围,请检查列名") sys.exit(1) for row in reader: if len(row) <= column_index: continue key = row[column_index] if key == "": key = "EMPTY" if key not in file_map: output_path = os.path.join(output_dir, f"{key}.csv") out_f = open(output_path, 'w', encoding='utf-8-sig', newline='') writer = csv.writer(out_f) if header: writer.writerow(header) file_map[key] = (out_f, writer) writer = file_map[key][1] writer.writerow(row) # 关闭所有文件句柄 for out_f, _ in file_map.values(): out_f.close() print(f"拆分完成,共生成 {len(file_map)} 个文件,按列索引 {column_index} 分组")这里有几个细节值得留意。输入文件打开时我用了utf-8-sig编码,这是为了兼容带 BOM 头的 CSV 文件。很多 Windows 下导出的 CSV 前面会带着看不见的\ufeff字符,如果你用utf-8打开,第一列的表头会多出一个特殊符号,后面做字段匹配时就容易出问题。用errors='replace'则是为了应对文件里混入的非法编码字符,宁可把它替换成特殊符号,也不要让整个脚本中途崩溃。
3.3 参数使用说明与实际运行效果
使用这个脚本的时候,你只需要改三个地方:输入文件路径、输出目录、拆分逻辑。这里以按行数拆分为例演示一下运行过程。
假设我有一个sales_data.csv,共 10 万行数据,带表头。我想每个文件放 5 万行数据,于是把脚本里的参数改成split_by_row_count("sales_data.csv", "./output", chunk_size=50000),然后运行。
脚本执行完,输出目录下会出现两个文件:split_001.csv和split_002.csv。每个文件都包含原始数据的表头,第一个文件是第 1 到 50000 行,第二个文件是第 50001 到 100000 行。整个过程耗时大概两秒,内存占用稳定在 20 MB 以内,完全不会出现卡顿。
如果把运行场景换成按字段拆分,比如我希望按region列把销售数据拆成“华东”“华南”“华北”三个文件,只需要先定位region在第几列,然后调用split_by_column_value("sales_data.csv", "./output_by_region", column_index=3)。运行完成后,输出目录里会出现华东.csv、华南.csv、华北.csv三个文件,每个文件里的数据是原文件中对应区域的所有记录。
这份脚本我用了很久,后来又加了按多个字段组合拆分的支持,其实原理一样,只是把字典的 key 换成多个字段拼接后的字符串。如果你有更复杂的规则,比如“按月拆分后再按地区拆”,完全可以在split_by_row_count的基础上继续扩展,写成嵌套循环或者文件句柄的双层字典都行,核心思路不变。
4. 高级需求:Python 处理 CSV 的其他实用技巧
4.1 2 维数组保存为 CSV
前面说了拆分,现在补充一个关联度很高的场景:怎么把 Python 里的二维数组保存为 CSV。比如你爬了一组网页数据,清洗之后得到一个 list of list,想存成表格文件给别人看,这个需求非常普遍。
import csv data = [ ["姓名", "城市", "销售额"], ["张三", "上海", 12000], ["李四", "北京", 9800], ["王五", "广州", 15300] ] with open("output.csv", "w", encoding="utf-8-sig", newline="") as f: writer = csv.writer(f) writer.writerows(data) print("CSV 文件保存成功")这里面有两点容易踩坑。第一,newline=""必须带上,否则在 Windows 下会在每行末尾多出一个空行,文件打开后看起来非常杂乱。第二,encoding建议用utf-8-sig而不是utf-8,因为utf-8-sig能确保 Excel 正确识别 UTF-8 编码,直接双击打开 CSV 不会出现中文乱码。如果你不传这个参数,默认可能是 GBK 或者系统区域相关编码,换台电脑运行结果就可能不一样。
4.2 使用 Pandas 处理 CSV 的取舍
很多资料推荐直接用 pandas 的read_csv读文件,再to_csv写文件。确实,pandas 做数据分析和处理非常方便,代码看起来也更简洁。
import pandas as pd df = pd.read_csv("sales_data.csv") # 按月份分组拆分的场景 for month, group in df.groupby("month"): group.to_csv(f"sales_{month}.csv", index=False, encoding="utf-8-sig")这段代码极其简洁,三行就能完成按月份拆分。但这里有个隐藏的性能问题:read_csv会把整个文件读进内存。如果你处理的文件是 200 MB,内存占用可能达到 600 MB 到 1 GB,因为 pandas 在内存中维护的数据结构比纯文本要大得多。对于 500 MB 以上的大文件,强烈不建议用 pandas 一次性读取,很容易把电脑搞死机。
那什么时候用 pandas?我个人的经验是:当数据量在 100 MB 以下,且拆分逻辑本身还伴随统计计算时,比如分组后顺便算个平均值,用 pandas 很合适。纯拆分、不做计算、文件又大,就用标准库的 csv 模块。标准库的 csv 不会一次性把所有数据加载到内存,而是迭代式读取,内存占用非常稳定,处理几个 G 的文件也不会报 MemoryError。
4.3 与数据库导入导出相关的路径联想
热词里提到了 SQL Server 的 CSV 导入、电力负荷数据气象 CSV 等场景,这些本质上都是在做数据格式转换和预处理。以 SQL Server 导入 CSV 为例,它通常要求 CSV 文件必须满足几个条件:第一行必须是列名、数据长度必须匹配目标表字段长度、日期格式必须统一。如果你的源文件太大不满足导入要求,先用前面说的拆分脚本按 5 万行或 10 万行切分,然后逐个导入,成功率会大幅提升。
电力负荷数据这类时序数据往往包含时间列、数值列和气象特征列,做模型预测前通常需要按日期分片存储。这时候用split_by_column_value按日期字段拆分,正好可以把每个时间窗口的数据单独保存,后续做训练集、验证集划分就方便多了。
5. 常见问题与复用技巧
5.1 踩坑实录与排查方法
先说编码问题。这是 CSV 处理里出现频率最高的坑,没有之一。如果你的 CSV 文件是用 Excel 另存的,编码可能是 ANSI(中文 Windows 下即 GBK),而不是 UTF-8。用utf-8去读 GBK 文件会直接报UnicodeDecodeError。遇到这种情况,最简单的办法是先用记事本打开文件,看能不能正常显示中文,然后在“另存为”里把编码改成 UTF-8。如果文件太大记事本也打不开,就写代码先探测编码,可以用chardet库自动识别,或者干脆用gbk编码尝试读取。
再说内存溢出问题。新手最容易犯的错误是用f.read()或者pd.read_csv()把整个文件读进内存,文件大了必然崩溃。正确做法是逐行迭代读取,像我们前面写的脚本那样。哪怕文件有 10 个 G,逐行读取方式占用的内存也不会超过几百 MB。
最后是列数不一致的问题。CSV 文件里如果某行数据中间包含了换行符,或者分隔符使用不一致,读取时容易出现 row 长度与表头不一致的情况。脚本里我是用errors='replace'硬扛过去的,但更稳妥的做法是在拆完文件后做一个快速校验,统计每个输出文件的行数、列数,输出汇总信息,方便人工核对。
5.2 让脚本更通用的优化方向
基础功能实现之后,我建议你做三件小事,让这个工具更好用。
把它封装成命令行工具。用argparse接收输入文件、输出目录、拆分方式、拆分参数,这样你就不需要每次改脚本源码了。甚至可以让它支持 Excel 文件的输入,直接操作.xlsx,但那个需要额外装openpyxl库,按需选择。
增加运行日志。在拆分时记录开始时间、结束时间、每个文件的生成路径、行数统计,输出到一个日志文件。这个对于后面排查数据漏发、重复发这类问题特别有用,尤其是数据量大、文件数量多的时候。
支持配置化规则。比如你每次按不同字段拆分,与其改代码里的column_index,不如把拆分规则写在一个config.json文件里,脚本启动时读取配置,灵活度和维护性都会好很多。
5.3 效率进阶:多进程拆分超大文件
如果你需要拆分的文件达到 5 G 以上,逐行扫描的方式虽然不爆内存,但耗时可能会偏长。这种情况下,可以先用csv模块快速读取文件,统计完总行数之后,记录每个分片文件对应的起始行偏移量,再使用多个进程并行处理不同的行区间。不过这种优化对大多数场景来说属于“过度设计”,我的建议是,文件在 3 G 以下老老实实用单进程逐行扫描,稳定、代码简单、不容易出错;真到 10 G 级别,先考虑是不是该换列式存储格式了,比如 Parquet,而不是继续和 CSV 较劲。
我从第一次写出拆分工具到现在,已经迭代了好几个版本,从最初的“只能按行数硬切”,到后来支持按字段分组、支持多进程并行,再到增加数据校验和日志输出,这个过程让我对“处理大文件”这件事越来越有底气。每当同事再丢给我一个 1 个多 G 的 CSV 说“帮我分一下”,我都可以从容地把脚本跑起来,几分钟后交付一份整洁的拆分结果,还会附带一行输出:共多少行、分成几个文件。这种感觉比手动复制粘贴痛快太多了。
如果你看完也想动手做一个属于自己的 CSV 拆分工具,我的建议是从最简单的按行数拆分开始,跑通之后再往上加功能。遇到编码问题别慌,先确认原文件的真实编码格式,再确定读取和写入时的编码参数。我现在的工作流里,遇到任何大 CSV 文件,第一反应永远是先拆分再做后续处理,这个习惯确实帮我省下了大量等待和排查的时间。
本文还有配套的精品资源,点击获取