分割合并
# 分割合并
按行数/按大小分割文件、分片合并、大日志切割、分片上传。
# 底层原理:流式 I/O vs 内存加载
分割大文件(如 10GB 日志)时,绝不能使用 f.read() 一次性读入内存——那会 OOM。正确做法是流式处理:每次从磁盘读一小块(chunk),处理后立即写入输出文件,内存占用恒定为 chunk 大小。
f.read(size) 的参数 size 决定了内核缓冲区的大小:太大会浪费内存,太小会增加系统调用次数。经验值:8KB~64KB 是最优平衡点(与文件系统的 block size 对齐)。shutil.copyfileobj 默认 bufsize 为 16KB(16×1024=16384 字节)——恰好是 ext4/XFS 等文件系统的默认块大小倍数。
二进制 vs 文本模式:按大小分割文件时必须用
'rb'/'wb'打开——二进制模式逐字节拷贝,不会引入换行符转换(\n↔\r\n)。文本模式分割二进制文件会损坏数据。
# 一、按行数分割
文本文件按行分割——适合日志分片。核心逻辑:逐行读取,每 N 行切换到新输出文件。enumerate(f, 1) 从 1 开始计数行号。
#!/usr/bin/env python3
from pathlib import Path
def split_by_lines(filepath, lines_per_file):
"""将文件分割为每 N 行的多个小文件——流式逐行,内存友好"""
base, ext = Path(filepath).stem, Path(filepath).suffix
with open(filepath) as f:
part = 1
out = open(f"{base}_part{part:03d}{ext}", 'w')
for i, line in enumerate(f, 1):
out.write(line)
if i % lines_per_file == 0:
out.close()
part += 1
out = open(f"{base}_part{part:03d}{ext}", 'w')
out.close()
print(f"✅ 分割为 {part} 个文件")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 二、按大小分割(分片上传场景)
云存储(S3/OSS/COS)通常要求单个分片 5MB~5GB,推荐 10~100MB。此实现完全按字节切割,不关心文件类型——对 JPEG/PDF/视频同样有效。
#!/usr/bin/env python3
import os
def split_by_size(filepath, chunk_size_mb=10):
"""按字节大小拆分为多个文件片——纯二进制,无损"""
chunk_size = chunk_size_mb * 1024 * 1024
base = os.path.basename(filepath)
with open(filepath, 'rb') as f:
part = 0
while True:
chunk = f.read(chunk_size)
if not chunk: # read 返回空字节 → EOF
break
part += 1
part_name = f"{base}.part{part:03d}"
with open(part_name, 'wb') as out:
out.write(chunk)
print(f" {part_name} ({len(chunk)/1024:.0f}KB)")
print(f"✅ 分割为 {part} 片")
def merge_parts(output_path, *part_files):
"""合并分片——严格按传入顺序拼接,不做任何变换"""
with open(output_path, 'wb') as out:
for pf in part_files:
with open(pf, 'rb') as f:
out.write(f.read())
print(f"✅ 合并完成 → {output_path}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 三、文件合并(流式优化版)
shutil.copyfileobj(fsrc, fdst) 内部使用 16KB 缓冲区循环读-写,避免了将整个源文件读入内存。对比 f.read() + out.write(),它在处理大文件时更稳定。
#!/usr/bin/env python3
import shutil, glob
def merge_files(output_path, input_pattern):
"""合并匹配模式的所有文件——sorted 保证顺序一致"""
files = sorted(glob.glob(input_pattern))
with open(output_path, 'wb') as out:
for fpath in files:
with open(fpath, 'rb') as fsrc:
shutil.copyfileobj(fsrc, out)
print(f" + {fpath}")
print(f"✅ 合并 {len(files)} 个文件 → {output_path}")
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 四、Shell 命令
split 的默认前缀是 x(生成 xaa, xab, ...),后缀两位字母。-d 切换为数字后缀,-a 控制后缀长度。
#!/bin/bash
# ===== split 命令 =====
split -l 1000 large.log part_ # 按行:每 1000 行一个文件
split -b 10M large.zip chunk_ # 按大小:每 10MB
split -d -a 3 -b 10M video.mp4 vid_ # -d 数字后缀 -a3 三位数
# ===== cat 合并——用 glob 顺序自然排序 =====
cat part_* > merged.txt # 字母序:part_aa, part_ab, ...
cat chunk_* > restored.zip
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
上次更新: 2026/06/17, 12:47:39