Python中写入磁盘前如何估算CSV数据集大小并按1GB阈值拆分
Python内存CSV按1GB阈值拆分最优实现
核心原则
绝对不要用内存对象大小估算磁盘CSV体积:sys.getsizeof()、pandas memory_usage(deep=True)统计的是Python对象在内存中的存储占用,和序列化后存为CSV文本的字节数偏差可达30%以上,完全不能作为拆分依据。
整个方案要保证单次遍历、无全量数据拷贝,时间复杂度控制在O(n),额外空间开销控制在KB级,不额外挤占内存。
具体实现步骤
- 先固定写入参数:确定CSV的编码(推荐统一用
utf-8)、换行符(跨平台兼容用\n即可)、分隔符(默认逗号),先单独序列化表头,拿到表头的准确字节长度,作为初始写入计数器的基准值。 - 逐行遍历内存中的数据集,不要批量序列化全表:每拿到一行,用Python标准库
csv模块完成单行序列化(自动处理字段转义、引号包裹、特殊字符转码逻辑,避免自己写规则漏算字节),直接读取序列化后字节串的长度,就是这行写到磁盘的真实大小。 - 阈值判断按行粒度执行:如果当前已写入字节数 + 当行字节数 ≤ 1GB(即
1024*1024*1024字节),就把该行写入第一个分片,累加计数器;一旦加入当行会超出1GB阈值,就把当前行及后续所有行全部写入第二个分片,两个分片开头都要写入表头保证格式合法。
注意:不要为了凑整1GB拆分单行内容,CSV是行存储格式,拆半行会导致文件损坏,最终第一个分片的大小是不超过1GB的最大值,符合常规拆分需求。
可直接运行的参考代码
import csv import io # 配置项,按实际场景修改 TARGET_1GB = 1024 * 1024 * 1024 FILE_ENCODING = "utf-8" LINE_TERMINATOR = "\n" DELIMITER = "," csv_headers = ["col1", "col2", "col3"] # 替换为你的数据集表头 in_memory_dataset = iter([...]) # 替换为内存中的数据集,转成迭代器逐行取数即可 # 预计算表头字节长度 buf = io.BytesIO() wrapper = io.TextIOWrapper(buf, encoding=FILE_ENCODING, newline=LINE_TERMINATOR) writer = csv.writer(wrapper, delimiter=DELIMITER, lineterminator=LINE_TERMINATOR) writer.writerow(csv_headers) wrapper.flush() header_bytes = buf.getvalue() current_written_size = len(header_bytes) # 复用缓冲区减少对象创建开销,提升速度 buf.seek(0) buf.truncate(0) with open("split_1gb_part.csv", "wb") as f_part1, open("split_rest_part.csv", "wb") as f_part2: # 两个分片都写入表头 f_part1.write(header_bytes) f_part2.write(header_bytes) write_to_part1 = True for row in in_memory_dataset: # 序列化单行获取真实磁盘字节数 writer.writerow(row) wrapper.flush() row_bytes = buf.getvalue() row_len = len(row_bytes) buf.seek(0) buf.truncate(0) if write_to_part1 and current_written_size + row_len <= TARGET_1GB: f_part1.write(row_bytes) current_written_size += row_len else: write_to_part1 = False f_part2.write(row_bytes)
复杂度说明
- 时间复杂度:O(n),全流程仅遍历一次数据集,每行仅做一次序列化操作,无重复计算,比先全量序列化再拆分的方案快一倍以上。
- 空间复杂度:O(1)(常数级),仅复用单个固定大小的行缓冲区,无全量数据拷贝,额外内存开销不超过1MB,不会给原有1.8GB的内存负载造成压力。
内容的提问来源于stack exchange,提问作者Payal Bhatia
相关产品推荐
相关产品推荐

