Skip to content
b0r1sshPublic

About

A small Go package for reading legacy Microsoft Excel .xls (BIFF8) workbooks. It lists worksheet names and streams each sheet's rows as an iterator without loading the whole sheet into memory with zero dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

go-xls

CI Go Reference codecov

A small Go package for reading legacy Microsoft Excel .xls (BIFF8) workbooks. It lists worksheet names and streams each sheet's rows as an iterator without loading the whole sheet into memory.

Features

  • Open workbooks from a file path or any io.ReaderAt
  • List the names of a workbook's visible worksheets
  • Stream rows of a sheet as an iter.Seq[Row] iterator as they are parsed, instead of buffering the whole sheet
  • String, numeric, date, boolean, and error cell values, including cached results from formulas and strings stored as shared, inline, or rich-text records, with spreadsheet-style column labels (A, B, ..., AA, ...)
  • Explicitly blank cells recorded by Excel are returned as TypeBlank cells
  • Each Row reports FirstCol/LastCol, the 0-based column range Excel recorded as having data, even if some of those columns are empty

Installation

go get github.com/b0r1ssh/go-xls

Usage

package main

import (
	"fmt"
	"log"

	"github.com/b0r1ssh/go-xls"
)

func main() {
	f, err := xls.OpenFile("report.xls")
	if err != nil {
		log.Fatal(err)
	}

	for _, name := range f.SheetNames() {
		fmt.Println("sheet:", name)
	}

	for row := range f.ReadRows(f.SheetNames()[0]) {
		if row.Error != nil {
			log.Fatal(row.Error)
		}

		for _, cell := range row.Cells {
			fmt.Printf("%s%d = %v\n", cell.Column, row.Index+1, cell.Value)
		}
	}
}

Row.Cells includes cells that Excel explicitly recorded as blank. Those cells have TypeBlank and a nil value. Empty positions with no cell record are omitted. Use Row.FirstCol and Row.LastCol to know the full column range Excel recorded for the row instead of inferring it from Cells:

for row := range f.ReadRows("Sheet1") {
	fmt.Printf("row %d spans columns %d to %d\n", row.Index, row.FirstCol, row.LastCol)
}

Cell types

Each Cell.Type reports the Go type stored in Cell.Value:

CellType Value type Example
TypeString string "hello"
TypeNumeric float64 3.14
TypeBoolean bool true
TypeDate time.Time 2024-01-01
TypeError string "#DIV/0!"
TypeBlank nil

Formula cells are returned with the result saved in the workbook; formulas are not recalculated, and their expressions are not exposed. The saved result uses the same cell types as other values: numeric results are TypeNumeric (or TypeDate when the cell has a date format), and string, boolean, blank, and error results use their corresponding types. Formula results cannot otherwise be distinguished from literal cell values.

A cell holding a formula error (#DIV/0!, #N/A, #REF!, etc.) is reported as TypeError rather than being silently dropped or folded into TypeString, so callers can distinguish an error value from literal text that happens to look like one:

for _, cell := range row.Cells {
	if cell.Type == xls.TypeError {
		log.Printf("%s%d has a formula error: %s", cell.Column, row.Index+1, cell.Value)
		continue
	}
	// ...
}

Reader benchmarks

benchmarks/ compares reading a large .xls file with this package and four other Go .xls readers, with results and instructions for running it yourself in benchmarks/README.md.

Release workflow for contributors

Merging a pull request into main automatically creates a Git tag and a GitHub release. Before merging, use at most one of these labels to select the semantic version increment:

  • patch: bug fixes and other backward-compatible changes (v1.2.3 to v1.2.4).
  • minor: new backward-compatible functionality (v1.2.3 to v1.3.0).
  • major: breaking API changes (v1.2.3 to v2.0.0).

When no release label is present, the workflow uses patch. Do not combine patch, minor, and major labels on one pull request; the release workflow will fail if more than one is present.

Use the skip-release label for documentation, CI, or other changes that do not need a new module version. When a pull request with this label is merged, the publish workflow skips the release entirely.

About

A small Go package for reading legacy Microsoft Excel .xls (BIFF8) workbooks. It lists worksheet names and streams each sheet's rows as an iterator without loading the whole sheet into memory with zero dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages