Skip to main content

流式 xlsx 读取器,基于 quick-xml + zip 实现真正的流式解析

Project description

stream_xlsx

流式 xlsx 读取器,支持 Rust 库、CLI 工具和 Python 绑定。基于 quick-xml + zlib-rs 实现真正的流式解析,不一次性将整张表载入内存

特点

  • 两种解析模式:
    • default 模式 — 单线程流式,内存最低(~2 GB,适合超大数据集)
    • fast 模式 (--fast) — 8 worker 并发解析,速度 ~3x faster(~4.8s vs ~15s)
  • 流式产出:逐 batch 产出 Polars DataFrame,100 万行 × 60 列(~660 MB)默认模式 ~15s,fast 模式 ~4.8s
  • 多 sheet 支持:打开后可查看所有 sheet 名称,按需切换,sharedStrings/styles 只解析一次
  • 惰性加载:open() 仅解析 sheet 列表;sharedStrings.xml / styles.xml 在首次读取时才加载
  • 类型推断:边读边推断列类型(Int → Float → String),空值不参与推断
  • skip_rows:支持跳过指定 0-based 行索引,不影响 header 解析
  • 日期支持:读取 xl/styles.xmlcellXfs + 自定义 numFmt,自动识别日期列
  • Shell 补全:内置 zsh / bash 自动补全生成

项目结构

sxlsx/              # CLI 工具(cargo build)
stream_xlsx/        # 纯 Rust 库(rlib)
stream_xlsx_py/     # pyo3 Python 绑定(maturin build)

安装

CLI

cargo build --release
# 二进制位于 target/release/sxlsx

Python

cd stream_xlsx_py
maturin build --release
pip install target/wheels/stream_xlsx_py-*.whl

使用

CLI

# 导出为 CSV(default 模式)
sxlsx tf csv data.xlsx --output out.csv

# 导出为 parquet,启用 fast 并发模式
sxlsx tf csv data.xlsx --fast --output out.csv

# 指定 sheet(按名称或索引)
sxlsx tf csv data.xlsx --sheet-name "Sheet1"
sxlsx tf csv data.xlsx --sheet-idx 0

# 统计行数(性能基准)
sxlsx test count data.xlsx
sxlsx test count data.xlsx --fast

# 生成测试文件:100 万行 × 60 列
sxlsx test test-file big.xlsx --rows 1000000 --col 60

# Shell 自动补全
sxlsx completion

fast 模式调优参数

--fast 启用并发解析,以下参数可微调(都有合理默认值,通常无需修改):

sxlsx tf csv data.xlsx --fast \
  --fast-parallelism 8 \     # worker 线程数
  --fast-chunk-size 1000 \   # 每 chunk cell 数
  --fast-queue-cap 1 \       # 队列容量倍数(queue = threads × mul + 1)
  --fast-temp-kb 1024 \      # 解压临时缓冲区(KB)
  --fast-buf-kb 1024         # ZIP 读取 BufReader 大小(KB)

Python

import stream_xlsx_py as sx

# 流式读取(默认 batch_size=10000)
reader = sx.read_xlsx("data.xlsx", batch_size=10000)
for df in reader:
    print(df.shape)

# 查看所有 sheet
print(reader.sheet_names())        # ["Sheet1", "Sheet2"]

# 切换 sheet
reader.select_sheet("Sheet2")
for df in reader:
    print(df.shape)

# 跳过指定行(0-based,不影响 header)
reader = sx.read_xlsx("data.xlsx", skip_rows=[1, 3, 5])
for df in reader:
    print(df.shape)

Benchmark

测试文件:test_100w_60c.xlsx(100 万行 × 60 列,通过 sxlsx test test-file --rows 1000000 --col 60 生成,约 659 MB,59,500,954 个 cell)

测试环境:macOS (Apple Silicon),Rust release(opt-level=3, lto=fat, codegen-units=1)

default 模式 vs fast 模式

Elapsed Time & Peak Memory Comparison

batch_size default 时间 fast 时间 加速 default 内存 fast 内存
1,000 15.10 s 4.82 s 3.1x 2,009 MB 2,685 MB
5,000 15.02 s 4.95 s 3.0x 2,012 MB 2,689 MB
10,000 15.28 s 4.84 s 3.2x 2,016 MB 2,692 MB
50,000 15.51 s 4.87 s 3.2x 2,050 MB 2,728 MB
100,000 15.15 s 4.77 s 3.2x 2,093 MB 2,772 MB
1,000,000 15.22 s 5.53 s 2.8x 2,867 MB 3,545 MB

关键观察:

  • 时间:fast 模式在所有 batch_size 下都 ~3x faster,且对 batch_size 不敏感——不像 default 模式那样需要挑 batch_size
  • 内存:fast 模式多消耗 ~30% 内存(并发 worker 的中间 buffer 和 channel queue),换来 3x 速度

内存曲线

Memory grid — all batch sizes

各 batch_size 下的 RSS 时间曲线,可以看到 default 和 fast 模式的内存稳定在一个平台上,没有持续增长——流式特性的体现。

具体某 batch_size(bs=10000 为推荐配置):

Memory curve — batch=10000

分模式查看:

default fast
default mode memory fast mode memory

Python 环境对比

在 Python 进程中对比 stream_xlsx_pypolars.read_excel:

方案 时间 峰值内存 备注
stream_xlsx_py fast (bs=10000) 4.77 s 2,746 MB 推荐:fast + 流式
stream_xlsx_py fast (bs=50000) 5.09 s 2,817 MB fast + 流式
stream_xlsx_py fast (bs=100000) 5.05 s 2,907 MB fast + 流式
stream_xlsx_py fast (bs=1000000) 5.73 s 3,599 MB fast + 全量
stream_xlsx_py (bs=10000) 14.48 s 2,068 MB default + 流式
stream_xlsx_py (bs=50000) 14.40 s 2,143 MB default + 流式
stream_xlsx_py (bs=100000) 14.59 s 2,907 MB default + 流式
stream_xlsx_py (bs=1000000) 14.57 s 3,586 MB default + 全量
polars + calamine 23.50 s 11,109 MB pl.read_excel(engine="calamine")
polars + xlsx2csv 92.64 s 10,902 MB pl.read_excel(engine="xlsx2csv")

结论:

  • fast 模式比 default 快 ~3x(14.5s → 4.8s),内存多 ~30% — 与 CLI 行为一致
  • stream_xlsx_py (default) 比 polars+calamine 快 1.6x,内存 低 81%
  • stream_xlsx_py fastpolars+calamine 快 4.9x,内存 低 75%
  • stream_xlsx_py fastpolars+xlsx2csv 快 19x,内存 低 75%
  • 内存曲线(注意 polars 峰值 11 GB vs stream_xlsx_py 稳定 2-3.6 GB):

Python memory usage over time

推荐配置

场景 推荐 理由
超大文件(>100 MB)、内存敏感 default + batch_size=10000 内存最低(~2 GB),无并发开销
通用大文件、追求速度 fast + batch_size=10000 ~3x faster,内存多 30%
全量加载到单个 DataFrame fast + batch_size=1000000 一次产出,无 batch 调度
Python 通用 stream_xlsx_py 默认参数 已经过调优,无需额外配置
Python 性能优先 stream_xlsx_py + fast=True ~3x 加速,内存代价 ~30%

工作原理

流式解析管线(default 模式)

ZIP file (deflate)
   │  后台线程逐 chunk 解压
   ▼
mpsc channel (256 KB chunks)
   │
   ▼
quick-xml 事件循环
   │  边解析边产出 Cell<Data>
   ▼
TypedCols (按类型 zero-copy 存储)
   │
   ▼ ① Int64/Float64/Bool/DateTime: from_vec_validity
   ▼ ② String: PlSmallStr → MutablePlString → StringView(零拷贝封装)
   Polars DataFrame

fast 模式:并发解析

fast 模式在 default 模式基础上,加上 8 worker 线程并发解析单元格:

ZIP file
   │  后台解压线程
   ▼
单线程 scanner (split boundaries + memchr)
   │  按 CHUNK_SIZE (默认 1000 cells) 切分
   ▼
crossbeam channel
   │  queue capacity = parallelism × 1 + 1 (天然背压)
   ▼
┌──────────┬──────────┬───── ┬──────────┐
│ worker 1 │ worker 2 │ ... │ worker 8 │
└──────────┴──────────┴───── ┴──────────┘
   │  并发解析各 cell 字节
   ▼
result channel
   │
   ▼
main thread (按 seq 顺序重组 + 输出 DataFrame)

关键技术点:

  • scanner 与解压并行:scanner 单线程扫 XML 边界,worker 解析纯 CPU 任务
  • CHUNK_SIZE=1000:每个 chunk 包含 1000 cells,在 channel 往返开销和 worker 利用率之间取得平衡
  • 天然背压:queue 容量 = parallelism + 1,worker 满载时 scanner 自动阻塞

零拷贝 DataFrame 构建

数字列(Int64 / Float64 / Bool / DateTime):

  • 边读边存入 Vec<T> + MutableBitmap
  • 通过 from_vec_validity 零拷贝转为 Arrow 数组
  • 避免 AnyValue 24 字节/值的开销

字符串列(SharedString / InlineString / DateTimeIso / DurationIso):

  • PlSmallStr 内部存储:长度 ≤22 字节直接 inline,无需堆分配
  • MutablePlString::push_value + freeze() 封装为 Utf8ViewArray,只标记所有权,不复制
  • DataFrame::from_chunks 零拷贝持有 Arrow 数组

局限性与已知问题

标题行仅支持单行

has_header=True 时,只把第一行作为列名。多级表头(例如合并单元格跨越两行形成"分类 + 字段"两层结构)目前不支持——会被当成数据行处理,导致第一行表头被合并到字符串列里。skip_rows 参数用于跳过数据行,对多级表头无帮助。

如需处理多级表头,可在读取后用 Polars 自行重塑列名,或预处理 xlsx 把多级表头合并为一行。

fast 模式共享字符串解析是简化版

为追求速度,fast 模式只识别最简单的 <si><t>...</t></si> 模式。遇到以下情况会回退到 default 模式的 streaming quick-xml 解析器:

XML 模式 示例 fast 模式行为
富文本(rich text) <si><r><t>bold</t></r><r><t>normal</t></r></si> ❌ 回退
CDATA 区段 <si><t><![CDATA[<value>]]></t></si> ❌ 回退
XML 实体 <si><t>a &amp; b</t></si> ❌ 回退
<t> 节点 <si><t>part1</t><t>part2</t></si> ❌ 回退
简单纯文本 <si><t>hello</t></si> ✅ 走快路径

实际影响:绝大多数由 Excel / WPS / Google Sheets 生成的 xlsx 文件用 simple 模式,fast 路径可覆盖 99%+ 场景。回退到 slow path 仅在共享字符串表本身较慢,不影响 95% 时间的 sheet XML 解析(那才是 fast 模式的真正优化点)。

如确需 fast 模式处理上述复杂情况,可手动将 xlsx 的 sharedStrings.xml "扁平化"(用 LibreOffice 重新保存通常就够了)。

构建

开发构建

# Rust 库 + CLI
cargo build

# 测试
cargo test --workspace

# Python wheel
cd stream_xlsx_py
maturin develop   # 开发模式,直接链接到 .venv
maturin build --release

CI

项目已配置 GitHub Actions(.github/workflows/),每次 push/PR 自动运行:

  1. cargo test --workspace
  2. cargo build --release(CLI artifact)
  3. maturin build --release(多平台 wheel artifact)

支持平台:Linux x64/ARM64、Windows x64/ARM64、macOS x64/ARM64。

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

stream_xlsx_py-0.3.0-cp39-abi3-win_arm64.whl (4.6 MB view details)

Uploaded CPython 3.9+Windows ARM64

stream_xlsx_py-0.3.0-cp39-abi3-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.9+Windows x86-64

stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.4 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

stream_xlsx_py-0.3.0-cp39-abi3-macosx_11_0_arm64.whl (5.1 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

stream_xlsx_py-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl (5.4 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file stream_xlsx_py-0.3.0-cp39-abi3-win_arm64.whl.

File metadata

File hashes

Hashes for stream_xlsx_py-0.3.0-cp39-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 219976c78f25542a76f870f5702b36dc90e5620b47825c10c5c8560c69697881
MD5 f8efa5857a54314325a71f26c68541a4
BLAKE2b-256 d631d0ffa1b1cc0e8c7826f05abd3e18d3d749033c950204ce40438de0bb8a3b

See more details on using hashes here.

Provenance

The following attestation bundles were made for stream_xlsx_py-0.3.0-cp39-abi3-win_arm64.whl:

Publisher: ci.yaml on LittleTomatoPotato/Stream-xlsx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stream_xlsx_py-0.3.0-cp39-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for stream_xlsx_py-0.3.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 8920ae7ceec0bab416e0f75a2f98ca47a9946bb161024dc05efc70a6ee7460f6
MD5 7c04d0aede7121ec33f5821d961b6d11
BLAKE2b-256 5c4b9a94354b116deff8faabd53a6ce3edd96134f0090966251cefdd676878c1

See more details on using hashes here.

Provenance

The following attestation bundles were made for stream_xlsx_py-0.3.0-cp39-abi3-win_amd64.whl:

Publisher: ci.yaml on LittleTomatoPotato/Stream-xlsx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7ed230547c12dbeed4e955174a685b447da9a83869f4896f347e450530d56fd6
MD5 ca7191bac5ed2f99e5bb82761a5a574b
BLAKE2b-256 05ff82655774cbc92834f16a1cc4a4fa7b3a3a8b1cd525d981a90f68643af172

See more details on using hashes here.

Provenance

The following attestation bundles were made for stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: ci.yaml on LittleTomatoPotato/Stream-xlsx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 501b21df70fdced193a50e0d836dc7292db4aeca605512431dbdeed59dbb9668
MD5 2316bf53d5db04fa4a0dba107b2a22cc
BLAKE2b-256 a32a50ab0e1269c50952b37549f1651f5139f13b8b7790dd0c17dd185ff74bb0

See more details on using hashes here.

Provenance

The following attestation bundles were made for stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: ci.yaml on LittleTomatoPotato/Stream-xlsx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stream_xlsx_py-0.3.0-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for stream_xlsx_py-0.3.0-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8fd0d9f44236f4199357de6a19e5953f6586bbc2a14e16ba7f85627c2f3eccf9
MD5 d37e5c8060ddfd05361addcee0287d60
BLAKE2b-256 9987df19e1d6fe3552d00de9f255de408bd7163b686fe68ff7e5f81c6d6cc9cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for stream_xlsx_py-0.3.0-cp39-abi3-macosx_11_0_arm64.whl:

Publisher: ci.yaml on LittleTomatoPotato/Stream-xlsx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stream_xlsx_py-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for stream_xlsx_py-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 a5d2062cfa4f6a3b9df50da2c629938e4f1839a85a184513b3dc4995bedcb4d2
MD5 748251d1b694e7b3b39d69098c696398
BLAKE2b-256 eee2ec08c937c311512626c2525a68531f48573e90a14f780a9e5d0930f85cce

See more details on using hashes here.

Provenance

The following attestation bundles were made for stream_xlsx_py-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl:

Publisher: ci.yaml on LittleTomatoPotato/Stream-xlsx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page