Создание рабочей книги XLSX с нуля
XlsxWriter создаёт новый Excel-файл .xlsx в памяти и записывает его на диск или в байтовый буфер — без шаблонов, без установки Excel, без COM-автоматизации. Это правильный инструмент, когда нужен полный контроль над листами, ячейками, стилями, объединениями и шириной столбцов, в отличие от маппинга через формато-независимый IR.
Писатель является частью того же ядра на Rust, которое читает XLSX со средним временем 5,0 мс и 100% успешным прохождением на корректных файлах Office, и доступен из Python, Rust, Go, C# и JavaScript (нативный Node).
Как создать XLSX-файл с нуля?
Создайте XlsxWriter, добавьте лист, запишите ячейки по координатам (row, col) (оба индекса с нуля), затем вызовите save. Значения ячеек могут быть str, int, float, bool или None. Одна и та же схема работает во всех привязках.
Rust
use office_oxide::xlsx::write::{XlsxWriter, CellData, CellStyle, NumberFormat};
fn main() -> office_oxide::Result<()> {
let mut wb = XlsxWriter::new();
let mut sheet = wb.add_sheet("Sales");
// Bold header row
let header = CellStyle::new().bold().background("D3D3D3");
sheet.set_cell_styled(0, 0, CellData::String("Item".into()), header.clone());
sheet.set_cell_styled(0, 1, CellData::String("Amount".into()), header);
// Data rows
sheet.set_cell(1, 0, CellData::String("Widget".into()));
sheet.set_cell(1, 1, CellData::Number(1500.0));
sheet.set_cell(2, 0, CellData::String("Gadget".into()));
sheet.set_cell(2, 1, CellData::Number(2400.0));
// SUM formula with currency formatting (omit the leading '=')
let currency = CellStyle::new().number_format(NumberFormat::Currency);
sheet.set_cell_styled(3, 1, CellData::Formula("SUM(B2:B3)".into()), currency);
// Merge a title banner across two columns
sheet.merge_cells(4, 0, 1, 2);
sheet.set_column_width(0, 20.0);
sheet.set_column_width(1, 15.0);
wb.save("sales.xlsx")?;
Ok(())
}
Python
from office_oxide import XlsxWriter
wb = XlsxWriter()
sheet = wb.add_sheet("Sales") # -> 0 (the sheet index)
# Header row — bold, light-grey fill
wb.set_cell_styled(sheet, 0, 0, "Item", bold=True, bg_color="D3D3D3")
wb.set_cell_styled(sheet, 0, 1, "Amount", bold=True, bg_color="D3D3D3")
# Data rows: row, col are 0-based
wb.set_cell(sheet, 1, 0, "Widget")
wb.set_cell(sheet, 1, 1, 1500.0)
wb.set_cell(sheet, 2, 0, "Gadget")
wb.set_cell(sheet, 2, 1, 2400.0)
# Widen the columns (Excel character units)
wb.set_column_width(sheet, 0, 20.0)
wb.set_column_width(sheet, 1, 15.0)
wb.save("sales.xlsx")
JavaScript
import { writeFileSync } from 'node:fs';
import { XlsxWriter } from 'office-oxide';
const wb = new XlsxWriter();
const sheet = wb.addSheet('Sales'); // 0-based index
// Header row — bold, grey fill (6-char hex string or null)
wb.setCellStyled(sheet, 0, 0, 'Item', true, 'D3D3D3');
wb.setCellStyled(sheet, 0, 1, 'Amount', true, 'D3D3D3');
// Data rows: value may be null, string, number, or boolean
wb.setCell(sheet, 1, 0, 'Widget');
wb.setCell(sheet, 1, 1, 1500.0);
wb.setCell(sheet, 2, 0, 'Gadget');
wb.setCell(sheet, 2, 1, 2400.0);
wb.mergeCells(sheet, 3, 0, 1, 2); // title banner: 1 row x 2 cols
wb.setColumnWidth(sheet, 0, 20.0);
wb.setColumnWidth(sheet, 1, 15.0);
wb.save('sales.xlsx');
// Or export to a Buffer:
const data = wb.toBytes();
writeFileSync('sales-copy.xlsx', data);
wb.close(); // release the native handle
Go
package main
import (
"os"
officeoxide "github.com/yfedoseev/office_oxide/go"
)
func main() {
wb := officeoxide.NewXlsxWriter()
defer wb.Close()
sheet := wb.AddSheet("Sales") // uint32, 0-based index
// Header row — bold with a grey fill
wb.SetCellStyled(sheet, 0, 0, "Item", true, "D3D3D3")
wb.SetCellStyled(sheet, 0, 1, "Amount", true, "D3D3D3")
// Data rows: value may be nil, string, float64, int, or bool
wb.SetCell(sheet, 1, 0, "Widget")
wb.SetCell(sheet, 1, 1, 1500.0)
wb.SetCell(sheet, 2, 0, "Gadget")
wb.SetCell(sheet, 2, 1, 2400.0)
wb.MergeCells(sheet, 3, 0, 1, 2) // title banner: 1 row x 2 cols
wb.SetColumnWidth(sheet, 0, 20.0)
wb.SetColumnWidth(sheet, 1, 15.0)
if err := wb.Save("sales.xlsx"); err != nil {
panic(err)
}
// Or export to bytes:
data, err := wb.ToBytes()
if err != nil {
panic(err)
}
_ = os.WriteFile("sales-copy.xlsx", data, 0o644)
}
C#
using OfficeOxide;
using var wb = new XlsxWriter();
uint sheet = wb.AddSheet("Sales"); // 0-based index
// Header row — bold, grey fill (6-char hex, no '#')
wb.SetCellStyled(sheet, 0, 0, "Item", bold: true, bgColor: "D3D3D3");
wb.SetCellStyled(sheet, 0, 1, "Amount", bold: true, bgColor: "D3D3D3");
// Data rows: value may be null, string, double, int, long, or bool
wb.SetCell(sheet, 1, 0, "Widget");
wb.SetCell(sheet, 1, 1, 1500.0);
wb.SetCell(sheet, 2, 0, "Gadget");
wb.SetCell(sheet, 2, 1, 2400.0);
wb.MergeCells(sheet, 3, 0, 1, 2); // title banner: 1 row x 2 cols
wb.SetColumnWidth(sheet, 0, 20.0);
wb.SetColumnWidth(sheet, 1, 15.0);
wb.Save("sales.xlsx");
// Or export to a byte[]:
byte[] data = wb.ToBytes();
File.WriteAllBytes("sales-copy.xlsx", data);
C
OfficeXlsxWriterHandle *w = office_xlsx_writer_new();
uint32_t s = office_xlsx_writer_add_sheet(w, "Sales"); /* 0-based index */
/* value_type in the writer: EMPTY=0, STRING=1, NUMBER=2 (no BOOLEAN) */
/* header row — bold + 6-char hex bg ("D3D3D3") or NULL */
office_xlsx_sheet_set_cell_styled(w, s, 0, 0, OFFICE_CELL_STRING, "Item", 0.0, true, "D3D3D3");
office_xlsx_sheet_set_cell_styled(w, s, 0, 1, OFFICE_CELL_STRING, "Amount", 0.0, true, "D3D3D3");
/* data rows */
office_xlsx_sheet_set_cell(w, s, 1, 0, OFFICE_CELL_STRING, "Widget", 0.0);
office_xlsx_sheet_set_cell(w, s, 1, 1, OFFICE_CELL_NUMBER, NULL, 1500.0);
office_xlsx_sheet_set_cell(w, s, 2, 0, OFFICE_CELL_STRING, "Gadget", 0.0);
office_xlsx_sheet_set_cell(w, s, 2, 1, OFFICE_CELL_NUMBER, NULL, 2400.0);
office_xlsx_sheet_merge_cells(w, s, 3, 0, 1, 2); /* title banner: row_span/col_span >= 1 */
office_xlsx_sheet_set_column_width(w, s, 0, 20.0); /* Excel char units */
office_xlsx_sheet_set_column_width(w, s, 1, 15.0);
int err = 0;
office_xlsx_writer_save(w, "sales.xlsx", &err);
/* or: uint8_t *b = office_xlsx_writer_to_bytes(w, &out_len, &err); ... office_oxide_free_bytes */
office_xlsx_writer_free(w);
Чтобы передать байты в HTTP-ответ или объектное хранилище вместо записи на диск, используйте to_bytes() (пример на Python ниже):
Python
data = wb.to_bytes() # -> bytes (a complete .xlsx ZIP)
with open("sales.xlsx", "wb") as f:
f.write(data)
Сигнатуры методов (Python)
| Метод | Сигнатура |
|---|---|
| Конструктор | XlsxWriter() |
| Добавить лист | add_sheet(name: str) -> int |
| Задать ячейку | set_cell(sheet: int, row: int, col: int, value: None|str|bool|int|float) -> None |
| Задать ячейку со стилем | set_cell_styled(sheet: int, row: int, col: int, value, bold: bool, bg_color: str|None = None) -> None |
| Объединить | merge_cells(sheet: int, row: int, col: int, row_span: int, col_span: int) -> None |
| Ширина столбца | set_column_width(sheet: int, col: int, width: float) -> None |
| Сохранить | save(path) -> None |
| Экспорт | to_bytes() -> bytes |
add_sheet возвращает индекс нового листа (с нуля) — передавайте его в качестве аргумента sheet во все остальные вызовы. Строки и столбцы нумеруются с нуля: ячейка A1 соответствует (row=0, col=0), B1 — (row=0, col=1).
Как объединить ячейки и создать заголовочный баннер?
merge_cells(sheet, row, col, row_span, col_span) объединяет прямоугольный диапазон с якорем в (row, col). Оба параметра span должны быть >= 1; значение объединённой области берётся из якорной ячейки.
Python
from office_oxide import XlsxWriter
wb = XlsxWriter()
s = wb.add_sheet("Report")
# A title that spans columns A through C of the first row
wb.set_cell_styled(s, 0, 0, "Q3 Revenue Report", bold=True, bg_color="FFE699")
wb.merge_cells(s, 0, 0, 1, 3) # 1 row tall, 3 columns wide
# Sub-header
wb.set_cell_styled(s, 1, 0, "Region", bold=True)
wb.set_cell_styled(s, 1, 1, "Q3", bold=True)
wb.set_cell_styled(s, 1, 2, "QoQ %", bold=True)
wb.set_cell(s, 2, 0, "NA")
wb.set_cell(s, 2, 1, 1_200_000)
wb.set_cell(s, 2, 2, 0.18)
wb.set_column_width(s, 0, 18.0)
wb.save("report.xlsx")
Сигнатуры по языкам
Rust — крейт Rust предоставляет более богатый строитель. add_sheet возвращает заимствованный дескриптор SheetData, а ячейки принимают значение CellData (String, Number, Boolean, Formula или Empty) и опциональный CellStyle. CellStyle — это строитель, поддерживающий bold(), italic(), background(), number_format(), align() и другие. Ключевые сигнатуры (SheetData):
pub fn set_cell(&mut self, row: usize, col: usize, value: CellData) -> &mut Self
pub fn set_cell_styled(&mut self, row: usize, col: usize, value: CellData, style: CellStyle) -> &mut Self
pub fn merge_cells(&mut self, row: usize, col: usize, row_span: usize, col_span: usize) -> &mut Self
pub fn set_column_width(&mut self, col: usize, width: f64) -> &mut Self
На XlsxWriter: add_sheet(name: &str) -> SheetData<'_>, save(path) -> Result<()> и write_to<W: Write + Seek>(writer) -> Result<()> для записи в память. Также доступно индексное зеркало (sheet_set_cell, sheet_set_cell_styled, sheet_merge_cells, sheet_set_column_width) — для тех, кто предпочитает плоскую форму, как в других привязках.
Python — XlsxWriter(), add_sheet(name: str) -> int, set_cell(sheet: int, row: int, col: int, value: None|str|bool|int|float) -> None, set_cell_styled(sheet: int, row: int, col: int, value, bold: bool, bg_color: str|None = None) -> None, merge_cells(sheet: int, row: int, col: int, row_span: int, col_span: int) -> None, set_column_width(sheet: int, col: int, width: float) -> None, save(path) -> None, to_bytes() -> bytes.
JavaScript — addSheet(name), setCell(sheet, row, col, value), setCellStyled(sheet, row, col, value, bold, bgColor = null), mergeCells(sheet, row, col, rowSpan, colSpan), setColumnWidth(sheet, col, width), save(path), toBytes(), close().
Go — NewXlsxWriter() *XlsxWriter, AddSheet(name string) uint32, SetCell(sheet, row, col uint32, value any), SetCellStyled(sheet, row, col uint32, value any, bold bool, bgColor string), MergeCells(sheet, row, col, rowSpan, colSpan uint32), SetColumnWidth(sheet, col uint32, width float64), Save(path string) error, ToBytes() ([]byte, error). Передайте "" в bgColor для отключения заливки.
C# — AddSheet(string name) -> uint, SetCell(uint sheet, uint row, uint col, object? value), SetCellStyled(uint sheet, uint row, uint col, object? value, bool bold, string? bgColor = null), MergeCells(uint sheet, uint row, uint col, uint rowSpan, uint colSpan), SetColumnWidth(uint sheet, uint col, double width), Save(string path), ToBytes() -> byte[]. XlsxWriter реализует IDisposable.
C — office_xlsx_writer_new() -> OfficeXlsxWriterHandle*, office_xlsx_writer_add_sheet(w, name) -> uint32_t, office_xlsx_sheet_set_cell(w, sheet, row, col, value_type, value_str, value_num), office_xlsx_sheet_set_cell_styled(w, sheet, row, col, value_type, value_str, value_num, bold, bg_color), office_xlsx_sheet_merge_cells(w, sheet, row, col, row_span, col_span), office_xlsx_sheet_set_column_width(w, sheet, col, width), office_xlsx_writer_save(w, path, &err), office_xlsx_writer_to_bytes(w, &out_len, &err), office_xlsx_writer_free(w). value_type: OFFICE_CELL_EMPTY (0), OFFICE_CELL_STRING (1) или OFFICE_CELL_NUMBER (2) — булевого типа ячейки у писателя нет.
Примечание: запись XLSX с нуля доступна в нативных привязках (Rust, Python, JavaScript/Node, Go, C#) и C ABI. Браузерная сборка WASM работает только на чтение — классов писателя в ней нет — поэтому создавайте рабочие книги через одну из перечисленных привязок и передавайте байты потоком.
Справочник по стилям
В привязках Python, Go, C#, JavaScript и C стилизованный сеттер предоставляет два параметра:
bold— булево значение, применяющее жирное начертание к ячейке.bg_color/bgColor— 6-символьная RGB hex-строка без ведущего#(например,"D3D3D3"для светло-серого,"FFE699"для янтарного). ПередайтеNone/""/nullдля отключения заливки.
Строитель CellStyle в Rust — наиболее полная поверхность: курсив, подчёркивание, цвет шрифта, размер шрифта, имя шрифта, числовые форматы (General, Integer, Decimal2, Currency, Percent, Percent2, Date, DateTime), горизонтальное выравнивание и перенос текста. Другие привязки напрямую открывают лишь два наиболее часто используемых параметра; для более сложной стилизации из этих языков используйте IR или постобработку.
Часто задаваемые вопросы
Строки и столбцы нумеруются с нуля или с единицы?
И row, и col — целые числа с нулевой нумерацией. Ячейка A1 — это (row=0, col=0), B1 — (row=0, col=1), A2 — (row=1, col=0). Индекс листа, возвращаемый add_sheet, тоже начинается с нуля.
Нужно ли устанавливать Excel или Microsoft Runtime?
Нет. XlsxWriter генерирует корректный OOXML .xlsx ZIP полностью на Rust. Никакой COM-автоматизации, JVM и системных зависимостей — тот же движок читает XLSX со средним временем 5,0 мс и 100% успехом на корректных файлах Office.
Как получить файл в виде байтов, не записывая на диск?
Вызовите to_bytes() (Python/C#/JS) или ToBytes() (Go) — будет возвращён полный байтовый буфер .xlsx. В Rust используйте write_to(writer) с любым Write + Seek-таргетом, например Cursor<Vec<u8>>. Это идеально для HTTP-ответов и загрузки в объектные хранилища.
Можно ли записывать формулы?
Да, в Rust через CellData::Formula("SUM(B2:B3)") (ведущий = опускается). Плоские привязки (Python/Go/C#/JS) пока поддерживают строковые, числовые, булевы и пустые значения ячеек через set_cell.
Как добавить второй рабочий лист?
Снова вызовите add_sheet — каждый вызов добавляет лист и возвращает следующий индекс с нуля. Передавайте этот индекс как аргумент sheet в последующие вызовы set_cell / merge_cells / set_column_width.
Смотрите также
- Создание документов из IR — одна схема, три целевых формата (DOCX/XLSX/PPTX)
- Редактирование ячеек XLSX на месте — изменение ячеек в существующей рабочей книге с помощью
EditableDocument - Извлечение данных из XLSX — чтение рабочей книги в структурированный IR