从零创建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。单元格值可以是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);
若要将字节流传递给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值(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十六进制字符串(例如浅灰色为"D3D3D3",琥珀色为"FFE699")。不填充时传入None/""/null。
Rust的CellStyle构建器功能最为完整——斜体、下划线、字体颜色、字体大小、字体名称、数字格式(General、Integer、Decimal2、Currency、Percent、Percent2、Date、DateTime)、水平对齐和文本换行。其他绑定直接公开最常用的两个选项;如需从这些语言进行更丰富的样式设置,可通过IR构建或进行后处理。
常见问题
行和列是从0开始还是从1开始?
row和col均为从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调用。
另请参阅
- 从IR创建文档 — 一个模式,三种目标格式(DOCX/XLSX/PPTX)
- 就地编辑XLSX单元格 — 使用
EditableDocument修改现有工作簿中的单元格 - 从XLSX提取数据 — 将工作簿读入结构化IR