Миграция с python-calamine (и calamine)
calamine — хорошо зарекомендовавший себя Rust-ридер XLSX/XLS; python-calamine — его Python-биндинг. Оба сосредоточены исключительно на таблицах.
Office Oxide в 2,8 раза быстрее python-calamine на XLSX (5,0 мс против 13,9 мс в среднем на 1 802 файлах) и показывает наивысшую долю успеха (97,8% против 96,6%). Он также добавляет полноценную поддержку DOCX, PPTX и устаревших DOC/PPT — форматов, которые calamine вообще не читает.
Когда мигрировать
Переходите, если что-то из этого про вас:
- Вам нужны также
.docx/.pptx/.doc/.ppt(calamine — только XLSX/XLS) - Хочется более широкого набора возможностей: вывод в Markdown / HTML, структурированный IR, шаблонизация через
EditableDocument - Доля успеха важнее небольшого выигрыша calamine по производительности в отдельных сценариях
- Вы на Python-биндинге и хотите меньше конверсий через FFI
Останьтесь на calamine, если:
- Вы читаете только
.xlsxи.xls - Зависите от специфичных для calamine API (
Reader::with_header_row,worksheet_range_atи т. п.) - Нужны выражения формул (calamine их отдаёт; IR Office Oxide — нет)
Установка (Python)
pip uninstall python-calamine
pip install office-oxide
Установка (Rust)
# Cargo.toml
[dependencies]
# Заменить:
# calamine = "0.30"
office_oxide = "0.1.0"
Сравнительная шпаргалка — Python
Открыть рабочую книгу
python-calamine
from python_calamine import CalamineWorkbook
wb = CalamineWorkbook.from_path("budget.xlsx")
office_oxide
from office_oxide import Document
with Document.open("budget.xlsx") as doc:
...
Итерация по листам
python-calamine
for name in wb.sheet_names:
sheet = wb.get_sheet_by_name(name)
for row in sheet.to_python():
print(row)
office_oxide
with Document.open("budget.xlsx") as doc:
ir = doc.to_ir()
for section in ir["sections"]:
print(f"# {section.get('title')}")
for el in section["elements"]:
if el["kind"] == "Table":
for row in el["rows"]:
print(row)
Прочитать один лист построчно
python-calamine
sheet = wb.get_sheet_by_name("Q4")
rows = sheet.to_python()
office_oxide
with Document.open("budget.xlsx") as doc:
table = next(
el for section in doc.to_ir()["sections"]
if section.get("title") == "Q4"
for el in section["elements"] if el["kind"] == "Table"
)
rows = table["rows"]
Более прямой путь:
with Document.open("budget.xlsx") as doc:
sheet = doc.as_xlsx().sheet("Q4")
rows = sheet.rows() # list[list[str]]
Имена листов
python-calamine
print(wb.sheet_names)
office_oxide
with Document.open("budget.xlsx") as doc:
print([s.name() for s in doc.as_xlsx().sheets()])
Сравнительная шпаргалка — Rust
Открыть и проитерировать
calamine
use calamine::{open_workbook, Xlsx, Reader};
let mut wb: Xlsx<_> = open_workbook("budget.xlsx")?;
for sheet_name in wb.sheet_names() {
if let Ok(range) = wb.worksheet_range(&sheet_name) {
for row in range.rows() {
println!("{row:?}");
}
}
}
office_oxide
use office_oxide::Document;
let doc = Document::open("budget.xlsx")?;
if let Some(xlsx) = doc.as_xlsx() {
for sheet in xlsx.sheets() {
for cell in sheet.cells() {
println!("{}: {:?}", cell.address(), cell.value());
}
}
}
Формат-независимый IR (у calamine эквивалента нет)
let doc = Document::open("budget.xlsx")?;
let ir = doc.to_ir();
serde_json::to_writer(std::io::stdout(), &ir)?;
Форма выхода такая же, как из .docx или .pptx — удобно, когда нижележащим потребителям не нужно знать исходный формат.
Запись в XLSX
calamine только читает. Office Oxide пишет ячейки XLSX через EditableDocument:
from office_oxide import EditableDocument
with EditableDocument.open("budget.xlsx") as ed:
ed.set_cell(0, "B5", 42_000)
ed.save("budget.xlsx")
Для полноценной сборки XLSX спускайтесь в xlsx::create::XlsxBuilder или используйте umya-spreadsheet / rust_xlsxwriter.
Производительность
| Библиотека | XLSX Среднее | p99 | Доля успеха |
|---|---|---|---|
| office_oxide | 5,0 мс | 40 мс | 97,8% |
| python-calamine | 13,9 мс | 183 мс | 96,6% |
| openpyxl | 94,5 мс | 698 мс | 96,2% |
| Библиотека | XLS Среднее | p99 | Доля успеха |
|---|---|---|---|
| office_oxide | 2,8 мс | 75 мс | 99,2% |
| python-calamine | 9,0 мс | 96 мс | 90,7% |
В чём разница
calamine возвращает типизированный Data-enum на каждую ячейку (Int, Float, String, Bool, DateTime, Empty, Error). IR Office Oxide схлопывает значения в строки; для типизированного доступа к ячейкам используйте формат-специфичный аксессор:
with Document.open("budget.xlsx") as doc:
for sheet in doc.as_xlsx().sheets():
for cell in sheet.cells():
print(cell.value(), cell.value_type()) # value_type: "string" | "number" | "boolean" | "empty"
См. также
- Миграция с openpyxl — для пользователей openpyxl
- Миграция с xlrd — для пользователей устаревшего
.xls - Бенчмарки производительности