流式 xlsx 读取器,基于 quick-xml + zip 实现真正的流式解析
Project description
stream_xlsx
流式 xlsx 读取器,支持 Rust 库、CLI 工具和 Python 绑定。基于 quick-xml + zip 实现真正的流式解析,不一次性将整张表载入内存。
项目结构
sxlsx/ # CLI 工具(cargo build)
stream_xlsx/ # 纯 Rust 库(rlib)
stream_xlsx_py/ # pyo3 Python 绑定(maturin build)
特点
- 流式读取:逐 batch 产出 Polars DataFrame,100 万行 × 60 列(~660 MB)只需 ~19 秒
- 低内存:峰值内存比 polars+calamine 降低 ~50–75%
- 多 sheet 支持:打开后可查看所有 sheet 名称,按需切换,共享字符串/styles 只解析一次
- 惰性加载:
open()仅解析 sheet 列表;sharedStrings.xml/styles.xml在首次读取时才加载 - skip_rows:支持跳过指定 0-based 行索引,不影响 header 解析
- 类型推断:边读边推断列类型(Int → Float → String),空值不参与推断
- 日期支持:读取
xl/styles.xml的cellXfs+ 自定义numFmt,自动识别日期列 - Shell 补全:内置 zsh / bash 自动补全生成
安装
CLI
cargo build --release
# 二进制位于 target/release/sxlsx
Python
cd stream_xlsx_py
maturin build --release
pip install target/wheels/stream_xlsx_py-*.whl
PyPI 安装(即将发布):
pip install stream-xlsx-py
使用
CLI
# 导出为 CSV
sxlsx tf csv data.xlsx --output out.csv
# 导出为 parquet
sxlsx tf parquet data.xlsx
# 指定 sheet(按名称或索引)
sxlsx tf csv data.xlsx --sheet-name "Sheet1"
sxlsx tf csv data.xlsx --sheet-idx 0
# 统计行数(性能基准)
sxlsx test count data.xlsx
# 生成测试文件:100 万行 × 60 列
sxlsx test test-file big.xlsx --rows 1000000 --col 60
# Shell 自动补全
sxlsx completion
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)
测试环境:macOS, Apple Silicon, release 构建。内存采样间隔 50 ms。
Python 环境对比
在 Python 进程中对比 stream_xlsx_py 流式遍历与 polars.read_excel 全量加载:
| 方案 | 时间 | 峰值内存 | 说明 |
|---|---|---|---|
| stream_xlsx_py (bs=10k) | 19.71 s | 2,621 MB | 流式遍历,batch=10k |
| stream_xlsx_py (bs=1M) | 18.93 s | 6,110 MB | 全量加载(单 batch) |
| polars + calamine | 24.00 s | 9,848 MB | pl.read_excel(engine="calamine") |
| polars + xlsx2csv | 92.41 s | 10,902 MB | pl.read_excel(engine="xlsx2csv") |
结论:
stream_xlsx_py比 polars+calamine 快 18%,内存 低 73%- 流式场景(bs=10k)内存仅 2.6 GB,全量加载(bs=1M)时与 polars 接近但仍低 2.3 GB
上图可见 polars calamine 在 ~24 s 达到 ~9.8 GB 峰值,而 stream_xlsx_py 流式场景全程稳定在 ~2.6 GB,全量加载时约 6.1 GB。
CLI 环境对比
逐 batch 遍历不保留中间结果(sxlsx test count):
| batch_size | 时间 | 峰值内存 | 批次数 |
|---|---|---|---|
| 1,000 | 18.74 s | 2,512 MB | 1000 |
| 5,000 | 18.75 s | 2,527 MB | 200 |
| 10,000 | 18.62 s | 2,549 MB | 100 |
| 50,000 | 18.80 s | 2,751 MB | 20 |
| 100,000 | 18.64 s | 3,692 MB | 10 |
| 1,000,000 | 18.87 s | 5,879 MB | 1 |
结论:
- batch_size 对时间影响极小(~18.6–18.9 s),瓶颈在 XML 解析和 ZIP 解压
- 流式场景(bs≤10k)峰值内存稳定在 ~2.5 GB;bs=100k 时升至 ~3.7 GB;全量加载(bs=1M)约 ~5.9 GB
上图展示了 6 个 batch size 条件下的内存曲线。流式场景(bs≤100k)曲线呈线性增长后进入平稳期;全量加载(bs=1M,最右列)内存显著升高,因为最终需要容纳完整的 DataFrame。
为什么内存更低?
- 惰性加载:
open()仅解析workbook.xml(~1 KB),sharedStrings.xml(~46 MB)和styles.xml(~20 KB)在首次读取时才加载 - 共享数据复用:多 sheet 切换时,sharedStrings/styles 只解析一次,通过
Arc共享 - 流式 XML 解析:
BufReader直接流式解析,无中间Vec<u8>缓冲 - 紧凑字符串存储:sharedStrings 使用
Vec<Box<str>>,每元素比Vec<String>省 24 bytes 容量开销
推荐配置
| 场景 | 推荐参数 |
|---|---|
| 超大文件(>100 MB) | batch_size=10000 |
| 中等文件(10–100 MB) | batch_size=10000 |
| 小文件(<10 MB) | batch_size=10000 |
| 全量加载到单个 DataFrame | batch_size=1000000(bs=1M 等效全量) |
小文件示例:
$ sxlsx --batchsize 10000 test count test_data.xlsx
11 145ms
batch_size=None:全量读取
batch_size 传入 None 时,内部自动计算为 sheet 的总数据行数,等效于一次性将全部数据读入单个 DataFrame。此时与 batch_size=1000000 行为相同。
// Rust API
let iter = DataFrameIter::new(None, "data.xlsx", None, None, true, None)?;
# Python API
reader = read_xlsx("data.xlsx", batch_size=None)
已知限制:cols → DataFrame 的额外拷贝
观察全量读取(bs=1M)的内存曲线可知,数据从内部 Cols 缓冲区转移到 DataFrame 时发生了一次数据拷贝而非所有权转移:
Cols内部以Vec<AnyValue>存储单元格数据finish_batch()调用Series::from_any_values_and_dtype()构造 PolarsSeries- 该 API 接收
&[AnyValue]引用,但内部需将数据转换为 Arrow 格式,导致一次完整拷贝 - 因此全量读取时内存峰值接近数据量的 2 倍(原始
Vec<AnyValue>+ Arrow 数组),随后原始Vec被 drop 才回落
这是与 Polars 之间仍需优化的一个点:理想情况下应能直接将 Vec<AnyValue> 零拷贝地转为 Arrow / Series,但目前 Polars 公共 API 不支持此操作。
构建
开发构建
# 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.2.0-cp39-abi3-win_arm64.whl.
File metadata
- Download URL: stream_xlsx_py-0.2.0-cp39-abi3-win_arm64.whl
- Upload date:
- Size: 4.7 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 |
7ed06210aa66203b6fa5231c24c2b92489adf62210bdb70575ccd6adca33d3d9
|
|
| MD5 |
eb4326fbe015420bc9cbc1280d94c318
|
|
| BLAKE2b-256 |
bfd49056fd677f8073aefae67a6c07eb429fcead1a2029a0f13654ac312d0d84
|
Provenance
The following attestation bundles were made for stream_xlsx_py-0.2.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.2.0-cp39-abi3-win_arm64.whl -
Subject digest:
7ed06210aa66203b6fa5231c24c2b92489adf62210bdb70575ccd6adca33d3d9 - Sigstore transparency entry: 1668444315
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.2.0-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: stream_xlsx_py-0.2.0-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 5.3 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 |
0f07920336950cdc5850f81c90d6da185dc0881f7ef36ab69fb4134d4740493f
|
|
| MD5 |
ceba49438374e638b874a79396ca1c7a
|
|
| BLAKE2b-256 |
8e1b0e7f6595047634e16844e2052944913cc0f8e9c5421d2382f5bcb54bb9e8
|
Provenance
The following attestation bundles were made for stream_xlsx_py-0.2.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.2.0-cp39-abi3-win_amd64.whl -
Subject digest:
0f07920336950cdc5850f81c90d6da185dc0881f7ef36ab69fb4134d4740493f - Sigstore transparency entry: 1668444151
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: stream_xlsx_py-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 5.7 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 |
4cca5fa20256b116b30fa1ab67a9271874ceb65be151b32f812884d7f881be76
|
|
| MD5 |
42b929d3e19705d5000bd0b714b70dd7
|
|
| BLAKE2b-256 |
70fa8d82965a94cecaa8ca9045e2ba6811320a2680dc3ab208bf94a79617f7a9
|
Provenance
The following attestation bundles were made for stream_xlsx_py-0.2.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.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
4cca5fa20256b116b30fa1ab67a9271874ceb65be151b32f812884d7f881be76 - Sigstore transparency entry: 1668444703
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: stream_xlsx_py-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 5.5 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 |
ef4c17f4ae6f314593aa2d33611604135b1287468e45c3e96fd9a1cdc4be6fcb
|
|
| MD5 |
98f31e3c9f9597d01ec94d8ea3309d96
|
|
| BLAKE2b-256 |
21b4099328302a21d1dd03c4a952e17cfcfe8d3719cd8e8af6db886b0a52db48
|
Provenance
The following attestation bundles were made for stream_xlsx_py-0.2.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.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
ef4c17f4ae6f314593aa2d33611604135b1287468e45c3e96fd9a1cdc4be6fcb - Sigstore transparency entry: 1668444526
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.2.0-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: stream_xlsx_py-0.2.0-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 5.0 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 |
391993bc1051d2d2010a7e9f0208f7025c52d1096cc639d278f3630e38695d79
|
|
| MD5 |
747c49ab09716eba2e69169dda17b487
|
|
| BLAKE2b-256 |
f66c9f7dc6a1177dbb961b1364daff839283f8c4003f7d9c3de6be50360557d5
|
Provenance
The following attestation bundles were made for stream_xlsx_py-0.2.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.2.0-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
391993bc1051d2d2010a7e9f0208f7025c52d1096cc639d278f3630e38695d79 - Sigstore transparency entry: 1668445002
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Trigger Event:
push
-
Statement type:
File details
Details for the file stream_xlsx_py-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: stream_xlsx_py-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 5.3 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 |
6bd7a5e957f53da49857e25f99351d162470e53f679cfc8ca9306a025800c422
|
|
| MD5 |
2b11dad93f63d0734428617e2e829e9c
|
|
| BLAKE2b-256 |
3e73481a129a935aa7558dbf78ac9083c4687ddbaf700adb043cb5b8b75cbe55
|
Provenance
The following attestation bundles were made for stream_xlsx_py-0.2.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.2.0-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
6bd7a5e957f53da49857e25f99351d162470e53f679cfc8ca9306a025800c422 - Sigstore transparency entry: 1668444826
- Sigstore integration time:
-
Permalink:
LittleTomatoPotato/Stream-xlsx@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/LittleTomatoPotato
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yaml@b4701cf75a52a190b9d7c9a69b1279325e81c89c -
Trigger Event:
push
-
Statement type: