流式 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.xml的cellXfs+ 自定义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 模式
| 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 速度
内存曲线
各 batch_size 下的 RSS 时间曲线,可以看到 default 和 fast 模式的内存稳定在一个平台上,没有持续增长——流式特性的体现。
具体某 batch_size(bs=10000 为推荐配置):
分模式查看:
| default | fast |
|---|---|
Python 环境对比
在 Python 进程中对比 stream_xlsx_py 与 polars.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 fast比polars+calamine快 4.9x,内存 低 75%stream_xlsx_py fast比polars+xlsx2csv快 19x,内存 低 75%- 内存曲线(注意 polars 峰值 11 GB vs stream_xlsx_py 稳定 2-3.6 GB):
推荐配置
| 场景 | 推荐 | 理由 |
|---|---|---|
| 超大文件(>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 数组 - 避免
AnyValue24 字节/值的开销
字符串列(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 & 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 自动运行:
cargo test --workspacecargo build --release(CLI artifact)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
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stream_xlsx_py-0.3.0-cp39-abi3-win_arm64.whl.
File metadata
- Download URL: stream_xlsx_py-0.3.0-cp39-abi3-win_arm64.whl
- Upload date:
- Size: 4.6 MB
- Tags: CPython 3.9+, Windows ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
219976c78f25542a76f870f5702b36dc90e5620b47825c10c5c8560c69697881
|
|
| MD5 |
f8efa5857a54314325a71f26c68541a4
|
|
| BLAKE2b-256 |
d631d0ffa1b1cc0e8c7826f05abd3e18d3d749033c950204ce40438de0bb8a3b
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stream_xlsx_py-0.3.0-cp39-abi3-win_arm64.whl -
Subject digest:
219976c78f25542a76f870f5702b36dc90e5620b47825c10c5c8560c69697881 - Sigstore transparency entry: 1727925861
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.3.0-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: stream_xlsx_py-0.3.0-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 5.4 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8920ae7ceec0bab416e0f75a2f98ca47a9946bb161024dc05efc70a6ee7460f6
|
|
| MD5 |
7c04d0aede7121ec33f5821d961b6d11
|
|
| BLAKE2b-256 |
5c4b9a94354b116deff8faabd53a6ce3edd96134f0090966251cefdd676878c1
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stream_xlsx_py-0.3.0-cp39-abi3-win_amd64.whl -
Subject digest:
8920ae7ceec0bab416e0f75a2f98ca47a9946bb161024dc05efc70a6ee7460f6 - Sigstore transparency entry: 1727926636
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Trigger Event:
push
-
Statement type:
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
- Download URL: stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 5.9 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ed230547c12dbeed4e955174a685b447da9a83869f4896f347e450530d56fd6
|
|
| MD5 |
ca7191bac5ed2f99e5bb82761a5a574b
|
|
| BLAKE2b-256 |
05ff82655774cbc92834f16a1cc4a4fa7b3a3a8b1cd525d981a90f68643af172
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
7ed230547c12dbeed4e955174a685b447da9a83869f4896f347e450530d56fd6 - Sigstore transparency entry: 1727926395
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 5.4 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
501b21df70fdced193a50e0d836dc7292db4aeca605512431dbdeed59dbb9668
|
|
| MD5 |
2316bf53d5db04fa4a0dba107b2a22cc
|
|
| BLAKE2b-256 |
a32a50ab0e1269c50952b37549f1651f5139f13b8b7790dd0c17dd185ff74bb0
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stream_xlsx_py-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
501b21df70fdced193a50e0d836dc7292db4aeca605512431dbdeed59dbb9668 - Sigstore transparency entry: 1727926535
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.3.0-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: stream_xlsx_py-0.3.0-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 5.1 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fd0d9f44236f4199357de6a19e5953f6586bbc2a14e16ba7f85627c2f3eccf9
|
|
| MD5 |
d37e5c8060ddfd05361addcee0287d60
|
|
| BLAKE2b-256 |
9987df19e1d6fe3552d00de9f255de408bd7163b686fe68ff7e5f81c6d6cc9cb
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stream_xlsx_py-0.3.0-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
8fd0d9f44236f4199357de6a19e5953f6586bbc2a14e16ba7f85627c2f3eccf9 - Sigstore transparency entry: 1727926258
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: stream_xlsx_py-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 5.4 MB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5d2062cfa4f6a3b9df50da2c629938e4f1839a85a184513b3dc4995bedcb4d2
|
|
| MD5 |
748251d1b694e7b3b39d69098c696398
|
|
| BLAKE2b-256 |
eee2ec08c937c311512626c2525a68531f48573e90a14f780a9e5d0930f85cce
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stream_xlsx_py-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
a5d2062cfa4f6a3b9df50da2c629938e4f1839a85a184513b3dc4995bedcb4d2 - Sigstore transparency entry: 1727926075
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@2a602c8e500b1ce41ef0a6e17f17064df1e1ca80 -
Trigger Event:
push
-
Statement type: