LLM Use with AI

Reading Workbooks

Opening a Workbook

wb, err := werkbook.Open("report.xlsx")
if err != nil {
    log.Fatal(err)
}

You can also open from memory or any random-access reader:

// If you already have the workbook bytes in memory:
wb, err := werkbook.OpenReader(bytes.NewReader(data))

// If you already have an io.ReaderAt:
wb, err := werkbook.OpenReaderAt(readerAt, size)

Reading Cell Values

Read individual cells by A1 reference:

sheet := wb.Sheet("Sheet1")

v, err := sheet.GetValue("A1")
if err != nil {
    log.Fatal(err)
}

fmt.Println(v.Raw())     // underlying Go value
fmt.Println(v.Type)      // werkbook.TypeString, TypeNumber, etc.
fmt.Println(v.IsEmpty()) // true if cell is empty

For formula cells, prefer GetValue. It evaluates dirty formulas before returning the result.

Checking Value Types

Use the Type field to handle different value types:

v, _ := sheet.GetValue("A1")

switch v.Type {
case werkbook.TypeNumber:
    fmt.Printf("Number: %f\n", v.Number)
case werkbook.TypeString:
    fmt.Printf("String: %s\n", v.String)
case werkbook.TypeBool:
    fmt.Printf("Bool: %t\n", v.Bool)
case werkbook.TypeError:
    fmt.Printf("Error: %s\n", v.String)
case werkbook.TypeEmpty:
    fmt.Println("(empty)")
}

Reading Formulas

formula, _ := sheet.GetFormula("D5")
if formula != "" {
    fmt.Println("Formula:", formula) // e.g. "SUM(D2:D4)"
}

// GetValue on a formula cell returns the computed result
value, _ := sheet.GetValue("D5")
fmt.Println("Result:", value.Raw())

Reading Workbook Metadata

fmt.Println(wb.SheetNames())
fmt.Println(wb.Date1904())
fmt.Println(wb.CalcProperties())

for _, table := range wb.Tables() {
    fmt.Println(table.DisplayName, table.Ref)
}

for _, dn := range wb.DefinedNames() {
    fmt.Println(dn.Name, dn.Value, dn.LocalSheetID)
}

If you just need sheet-local tables:

for _, table := range sheet.Tables() {
    fmt.Println(table.DisplayName)
}

Iterating Over Rows

Use the Rows() iterator to walk through all non-empty rows:

for row := range sheet.Rows() {
    fmt.Printf("Row %d:\n", row.Num())

    for _, cell := range row.Cells() {
        colName := werkbook.ColumnNumberToName(cell.Col())
        ref := fmt.Sprintf("%s%d", colName, row.Num())
        v, _ := sheet.GetValue(ref)
        fmt.Printf("  %s = %v\n", ref, v.Raw())
    }
}

Rows with a custom height but no cells are also included by Rows().

Sheet Dimensions

maxRow := sheet.MaxRow() // highest row currently present on the sheet
maxCol := sheet.MaxCol() // highest column containing a cell

fmt.Printf("Data range: A1:%s%d\n",
    werkbook.ColumnNumberToName(maxCol), maxRow)

Listing Sheets

for _, name := range wb.SheetNames() {
    sheet := wb.Sheet(name)
    fmt.Printf("Sheet %q: %d rows, %d cols\n",
        name, sheet.MaxRow(), sheet.MaxCol())
}

Reading Dates Safely

Cells that look like dates are still stored as numbers. Use the cell style to decide whether a numeric value should be interpreted as a date:

v, _ := sheet.GetValue("B2")
style, _ := sheet.GetStyle("B2")

if v.Type == werkbook.TypeNumber && style != nil &&
    werkbook.IsDateFormat(style.NumFmt, style.NumFmtID) {
    t := werkbook.ExcelSerialToTime(v.Number)
    fmt.Println(t)
}

ExcelSerialToTime interprets serials in the 1900 date system. If wb.Date1904() is true, keep that difference in mind when converting raw serials yourself.

Reading Styles

style, _ := sheet.GetStyle("A1")
if style != nil {
    if style.Font != nil {
        fmt.Printf("Font: %s, size %.0f\n", style.Font.Name, style.Font.Size)
        fmt.Printf("Bold: %t\n", style.Font.Bold)
    }
    if style.Fill != nil {
        fmt.Printf("Fill color: #%s\n", style.Fill.Color)
    }
}

Reading Merges and Visibility

fmt.Println(sheet.Visible())

for _, mr := range sheet.MergeCells() {
    fmt.Println(mr.Start, mr.End)
}

Printing a Sheet

For quick debugging, print a human-readable table:

sheet.PrintTo(os.Stdout)