Skip to content

从零创建XLSX工作簿

XlsxWriter在内存中构建全新的Excel .xlsx文件,并将其写入磁盘或字节缓冲区——无需模板、无需安装Excel、无需COM自动化。当你需要完全控制工作表、单元格、样式、合并和列宽时,这是最合适的工具,而无需通过格式无关IR进行映射。

该写入器是同一Rust核心的组成部分,能以平均5.0ms、对有效Office文件100%通过率的性能读取XLSX,并在Python、Rust、Go、C#和JavaScript(原生Node)中均有绑定。

如何从零创建XLSX文件?

构造一个XlsxWriter,添加工作表,通过(row, col)(均从0开始)写入单元格,然后调用save。单元格值可以是strintfloatboolNone。相同的模式在所有绑定中均适用。

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);

若要将字节流传递给Web响应或对象存储,而不写入磁盘,请使用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返回新工作表从0开始的索引——将其作为sheet参数传递给所有其他调用。行和列均从0开始,因此单元格A1对应(row=0, col=0)B1对应(row=0, col=1)

如何合并单元格并创建标题横幅?

merge_cells(sheet, row, col, row_span, col_span)合并以(row, col)为锚点的矩形区域。两个跨度均须>= 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 crate提供了更丰富的构建器。add_sheet返回一个借用的SheetData句柄,单元格接受CellData值(StringNumberBooleanFormulaEmpty)以及可选的CellStyleCellStyle是一个支持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_cellsheet_set_cell_styledsheet_merge_cellssheet_set_column_width)。

PythonXlsxWriter()add_sheet(name: str) -> intset_cell(sheet: int, row: int, col: int, value: None|str|bool|int|float) -> Noneset_cell_styled(sheet: int, row: int, col: int, value, bold: bool, bg_color: str|None = None) -> Nonemerge_cells(sheet: int, row: int, col: int, row_span: int, col_span: int) -> Noneset_column_width(sheet: int, col: int, width: float) -> Nonesave(path) -> Noneto_bytes() -> bytes

JavaScriptaddSheet(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()

GoNewXlsxWriter() *XlsxWriterAddSheet(name string) uint32SetCell(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) errorToBytes() ([]byte, error)。不填充时将bgColor传入""

C#AddSheet(string name) -> uintSetCell(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

Coffice_xlsx_writer_new() -> OfficeXlsxWriterHandle*office_xlsx_writer_add_sheet(w, name) -> uint32_toffice_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_typeOFFICE_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十六进制字符串(例如浅灰色为"D3D3D3",琥珀色为"FFE699")。不填充时传入None / "" / null

Rust的CellStyle构建器功能最为完整——斜体、下划线、字体颜色、字体大小、字体名称、数字格式(GeneralIntegerDecimal2CurrencyPercentPercent2DateDateTime)、水平对齐和文本换行。其他绑定直接公开最常用的两个选项;如需从这些语言进行更丰富的样式设置,可通过IR构建或进行后处理。

常见问题

行和列是从0开始还是从1开始? rowcol均为从0开始的整数。单元格A1对应(row=0, col=0)B1对应(row=0, col=1)A2对应(row=1, col=0)add_sheet返回的工作表索引也从0开始。

需要安装Excel或任何Microsoft运行时吗? 不需要。XlsxWriter完全用Rust生成有效的OOXML .xlsx ZIP文件。无需COM自动化、JVM,也没有系统依赖——同一引擎以平均5.0ms、对有效Office文件100%通过率的性能读取XLSX。

如何以字节流形式获取文件而非写入磁盘? 调用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——每次调用都会追加一个工作表并返回下一个从0开始的索引。将该索引作为sheet参数传递给后续的set_cell / merge_cells / set_column_width调用。

另请参阅