Some tools (older import forms, email services, spreadsheet apps) choke on big CSV files. A common fix is to split the file into smaller parts, with the header row repeated in every part. Here is a small script that does exactly that using only Python's standard library.
Why not just split -l?
The Unix split command cuts by line count. That breaks in two ways:
- The header only ends up in the first file.
- A quoted field that contains a line break can be cut in half.
Python's csv module reads whole records, so multi-line fields stay intact.
The script
import csv
import sys
from pathlib import Path
def split_csv(src, rows_per_file, out_dir="."):
src = Path(src)
out_dir = Path(out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
written = []
with src.open(newline="", encoding="utf-8-sig") as f:
reader = csv.reader(f)
header = next(reader)
writer = None
out = None
count = 0
part = 0
for row in reader:
if writer is None or count >= rows_per_file:
if out:
out.close()
part += 1
path = out_dir / f"{src.stem}_part{part:03d}.csv"
out = path.open("w", newline="", encoding="utf-8")
writer = csv.writer(out)
writer.writerow(header)
written.append(path)
count = 0
writer.writerow(row)
count += 1
if out:
out.close()
return written
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.exit("Usage: python split_csv.py input.csv rows_per_file [out_dir]")
for p in split_csv(sys.argv[1], int(sys.argv[2]), *(sys.argv[3:4])):
print(p)
Usage
python split_csv.py products.csv 1000 parts/
This writes products_part001.csv, products_part002.csv and so on into parts/, each with at most 1000 data rows plus the header.
Quick test
I tested it with a 25-row file that included fields containing commas and line breaks, split at 10 rows. Result: three files with 10, 10 and 5 data rows, every file starting with the same header, and 25 rows in total, so nothing was lost or duplicated.
Notes
-
utf-8-sigon input strips a BOM if Excel added one; the output is plain UTF-8. - The file is streamed row by row, so memory use stays small even for large files.
- If your target tool needs a BOM (some Excel versions do), change the output encoding to
utf-8-sig.
If you only need the rows count per part, adjust the second argument. That is all there is to it.
Top comments (0)