calamine для Rust: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Библиотека calamine для Rust: чтение Excel файлов

1. Что это и зачем нужна

calamine — это высокопроизводительная библиотека для чтения файлов Excel (форматы .xlsx, .xls, .xlsb, .ods) на языке Rust. Она написана на чистом Rust без внешних зависимостей, что делает её идеальным выбором для проектов, где важны скорость, безопасность памяти и кроссплатформенность. В отличие от многих других библиотек, calamine не требует установки Microsoft Office или Java Runtime Environment.

Основное назначение calamine — извлечение данных из электронных таблиц. Она поддерживает чтение ячеек, строк, столбцов, листов, а также работу с формулами, датами, числами и строками. Библиотека особенно полезна для автоматизации обработки отчётов, импорта данных из Excel в базы данных, анализа больших таблиц и создания конвейеров обработки данных (ETL).

calamine спроектирована с акцентом на производительность: она использует потоковое чтение и ленивую загрузку данных, что позволяет обрабатывать файлы размером в сотни мегабайт без значительного потребления оперативной памяти. Библиотека активно поддерживается сообществом Rust и используется в таких известных проектах, как rust-xlsxwriter и tauri.

2. Установка

Для добавления calamine в проект Rust выполните команду в терминале:

cargo add calamine

Или вручную добавьте зависимость в файл Cargo.toml:

[dependencies]
calamine = "0.24"

После этого выполните cargo build, чтобы загрузить и скомпилировать библиотеку. Убедитесь, что у вас установлен Rust (рекомендуется версия 1.65+).

3. Быстрый старт

Минимальный пример чтения данных из Excel файла:

use calamine::{open_workbook, Reader, Xlsx};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Открываем файл
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx")?;
    
    // Получаем первый лист
    let sheet_name = workbook.sheet_names()[0].clone();
    let range = workbook.worksheet_range(&sheet_name)?;
    
    // Выводим содержимое
    for row in range.rows() {
        for cell in row {
            print!("{:?}\t", cell);
        }
        println!();
    }
    
    Ok(())
}

Этот код открывает файл data.xlsx, читает первый лист и выводит все ячейки в консоль. Обратите внимание на использование open_workbook — универсальной функции для открытия файлов любого поддерживаемого формата.

4. Основные методы и функции

4.1. open_workbook

Сигнатура: pub fn open_workbook<P: AsRef<Path>, T: Reader<BufReader<File>>>(path: P) -> Result<T, CalamineError>

Что делает: Открывает Excel файл по указанному пути и возвращает рабочую книгу (workbook) указанного типа. Тип возвращаемого значения зависит от формата файла: Xlsx, Xls, Xlsb или Ods.

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("report.xlsx").expect("Cannot open file");
    println!("File opened successfully");
}

4.2. sheet_names

Сигнатура: fn sheet_names(&self) -> &[String]

Что делает: Возвращает список названий всех листов в рабочей книге. Порядок соответствует порядку листов в файле.

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx").unwrap();
    let names = workbook.sheet_names();
    println!("Листы: {:?}", names);
}

4.3. worksheet_range

Сигнатура: fn worksheet_range(&mut self, name: &str) -> Result<Range<Data>, CalamineError>

Что делает: Загружает данные указанного листа в память и возвращает объект Range, содержащий все ячейки. Это основной метод для получения данных.

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx").unwrap();
    let range = workbook.worksheet_range("Sheet1").unwrap();
    println!("Загружено {} строк", range.height());
}

4.4. rows

Сигнатура: fn rows(&self) -> Rows<'_, Data>

Что делает: Возвращает итератор по строкам диапазона. Каждая строка представлена как срез ячеек &[Data]. Позволяет эффективно обходить таблицу построчно.

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx").unwrap();
    let range = workbook.worksheet_range("Sheet1").unwrap();
    
    for (i, row) in range.rows().enumerate() {
        println!("Строка {}: {} ячеек", i, row.len());
    }
}

4.5. cells

Сигнатура: fn cells(&self) -> Cells<'_, Data>

Что делает: Возвращает итератор по всем ячейкам диапазона. Каждая ячейка представлена как (&Data, &Cell), где Cell содержит информацию о позиции (строка, столбец).

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx").unwrap();
    let range = workbook.worksheet_range("Sheet1").unwrap();
    
    for (data, cell) in range.cells() {
        println!("Ячейка ({}, {}): {:?}", cell.row, cell.col, data);
    }
}

4.6. get_value

Сигнатура: fn get_value(&self, row: u32, col: u32) -> Option<&Data>

Что делает: Позволяет получить значение конкретной ячейки по её координатам (строка, столбец). Возвращает None, если ячейка пуста или находится за пределами диапазона.

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx").unwrap();
    let range = workbook.worksheet_range("Sheet1").unwrap();
    
    if let Some(value) = range.get_value(0, 0) {
        println!("Значение A1: {:?}", value);
    }
}

4.7. height и width

Сигнатура: fn height(&self) -> u32 и fn width(&self) -> u32

Что делает: Возвращают количество строк (height) и столбцов (width) в диапазоне. Полезно для проверки размеров таблицы перед обработкой.

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx").unwrap();
    let range = workbook.worksheet_range("Sheet1").unwrap();
    
    println!("Таблица {}x{}", range.height(), range.width());
}

4.8. Data::get_string и Data::get_float

Сигнатура: fn get_string(&self) -> Option<&String> и fn get_float(&self) -> Option<f64>

Что делает: Методы для безопасного извлечения значений определённого типа из ячейки. get_string возвращает строку, get_float — число с плавающей точкой. Возвращают None, если тип не совпадает.

use calamine::{open_workbook, Xlsx, Data};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx").unwrap();
    let range = workbook.worksheet_range("Sheet1").unwrap();
    
    for row in range.rows() {
        if let Some(name) = row[0].get_string() {
            if let Some(salary) = row[1].get_float() {
                println!("{}: {}", name, salary);
            }
        }
    }
}

4.9. worksheet_formula

Сигнатура: fn worksheet_formula(&mut self, name: &str) -> Result<Range<Formula>, CalamineError>

Что делает: Загружает формулы указанного листа (вместо вычисленных значений). Возвращает диапазон, содержащий строки с формулами. Полезно для анализа структуры таблиц.

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("formulas.xlsx").unwrap();
    let formulas = workbook.worksheet_formula("Sheet1").unwrap();
    
    for (formula, cell) in formulas.cells() {
        println!("Ячейка ({}, {}): {}", cell.row, cell.col, formula);
    }
}

4.10. has_next_sheet и next_sheet

Сигнатура: fn has_next_sheet(&self) -> bool и fn next_sheet(&mut self) -> bool

Что делает: Позволяют последовательно перебирать листы рабочей книги без необходимости знать их названия. has_next_sheet проверяет наличие следующего листа, next_sheet переключает на него.

use calamine::{open_workbook, Xlsx};

fn main() {
    let mut workbook: Xlsx<_> = open_workbook("data.xlsx").unwrap();
    
    while workbook.has_next_sheet() {
        workbook.next_sheet().unwrap();
        let range = workbook.worksheet_range_at(workbook.current_sheet_index().unwrap()).unwrap();
        println!("Лист {}: {} строк", workbook.current_sheet_index().unwrap(), range.height());
    }
}

5. Реальный практический пример

Ниже представлен полноценный пример программы, которая читает Excel-файл с данными о сотрудниках, фильтрует их по отделу и выводит статистику. Файл employees.xlsx содержит столбцы: Name, Department, Salary, HireDate.

use calamine::{open_workbook, Reader, Xlsx, Data};
use std::collections::HashMap;

#[derive(Debug)]
struct Employee {
    name: String,
    department: String,
    salary: f64,
    hire_date: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Открываем файл
    let mut workbook: Xlsx<_> = open_workbook("employees.xlsx")?;
    let sheet_name = workbook.sheet_names()[0].clone();
    let range = workbook.worksheet_range(&sheet_name)?;
    
    // 2. Парсим данные
    let mut employees: Vec<Employee> = Vec::new();
    let mut errors: Vec<String> = Vec::new();
    
    for (row_idx, row) in range.rows().enumerate().skip(1) { // Пропускаем заголовок
        if row.len() < 4 {
            errors.push(format!("Строка {}: недостаточно столбцов", row_idx + 1));
            continue;
        }
        
        let name = match row[0].get_string() {
            Some(s) => s.clone(),
            None => {
                errors.push(format!("Строка {}: имя не является строкой", row_idx + 1));
                continue;
            }
        };
        
        let department = match row[1].get_string() {
            Some(s) => s.clone(),
            None => {
                errors.push(format!("Строка {}: отдел не является строкой", row_idx + 1));
                continue;
            }
        };
        
        let salary = match row[2].get_float() {
            Some(f) => f,
            None => {
                errors.push(format!("Строка                    

Рекомендаций