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)