Automate the Boring Stuff with Go, Ch. 9: Excel Files
// written in 2023: tools, versions and prices may have changed since.
Part of Automate the Boring Stuff with Go, a beginner-friendly guide to programming in Go.
XLSX GO
package main
import (
"fmt"
"io"
"log"
"os"
"github.com/tealeg/xlsx"
)
func main() {
pwdDir, _ := os.Getwd()
os.Mkdir(pwdDir+"/log", 0740)
fLog, err := os.OpenFile(pwdDir+"/log/log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
check(err, fLog)
excelFileName := pwdDir + "/example.xlsx"
xlFile, err := xlsx.OpenFile(excelFileName)
check(err, fLog)
i := xlFile.Sheets[0]
fmt.Println(i.Name)
y := i.Cell(0, 0)
a := y.String()
fmt.Println(a)
k := i.MaxRow
for u := 0; u < k; u++ {
sd := i.Cell(u, 1)
ss := sd.String()
fmt.Println(ss)
}
}
// err check to log
func check(err error, fLog *os.File) {
if err != nil {
log.Println(err)
}
log.SetOutput(io.MultiWriter(fLog, os.Stdout))
}
Census Population Data
package main
import (
"encoding/json"
"flag"
"github.com/tealeg/xlsx"
"log"
"os"
)
func main() {
openExcelPtr := flag.String("open", "census_pop_data.xlsx", "for opening excel files.")
saveToJson := flag.String("save", "data_to.json", "json file to save the excel file data to.")
pwdDir, _ := os.Getwd()
flag.Parse()
fLog, err := os.OpenFile(pwdDir+`/.log`, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
log.Fatalln(err)
}
defer fLog.Close()
log.SetOutput(fLog)
exitData, err := os.OpenFile(pwdDir+`/`+*saveToJson, os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
log.Fatalln(err)
}
defer exitData.Close()
excelFileName := pwdDir + "/" + *openExcelPtr
log.Printf("excel file name: %v", excelFileName)
wb, err := xlsx.OpenFile(excelFileName)
if err != nil {
log.Fatalf("Error opening file %v", err)
}
sheet := wb.Sheet["Population by Census Tract"]
log.Printf("Sheet name: %v", sheet.Name)
k := sheet.MaxRow
type Key struct {
State, Country string
}
popData := make(map[Key]float64)
tractsData := make(map[Key]int)
for u := 1; u < k; u++ {
state := sheet.Cell(u, 1)
country := sheet.Cell(u, 2)
pop := sheet.Cell(u, 3)
nameStateStr := state.String()
countryStr := country.String()
popFl, err := pop.Float()
if err != nil {
log.Fatalf("error %v", err)
}
popData[Key{nameStateStr, countryStr}] += popFl
tractsData[Key{nameStateStr, countryStr}]++
}
type SaveData struct {
Name Key `json:"state and country name"`
Pop float64 `json:"populations"`
Tracts int `json:"tracts"`
}
log.Printf("saving json JSON %s", exitData.Name())
newStr := "\n"
for keys := range popData {
saveData2D := &SaveData{
Name: keys,
Pop: popData[keys],
Tracts: tractsData[keys]}
saveData2B, _ := json.Marshal(saveData2D)
saveData2B = append(saveData2B, newStr...)
if _, err := exitData.Write(saveData2B); err != nil {
log.Panicf("error savying json %v", err)
}
}
log.Println("done ")
}
Save Excel
package main
import (
"flag"
"github.com/tealeg/xlsx"
"log"
"os"
)
func main() {
saveFilePtr := flag.String("save", "files.xlsx", "")
flag.Parse()
pwdDir, _ := os.Getwd()
fLog, err := os.OpenFile(pwdDir+`/.log`, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
log.Fatalln(err)
}
defer fLog.Close()
log.SetOutput(fLog)
var (
file *xlsx.File
sheet *xlsx.Sheet
row *xlsx.Row
cell *xlsx.Cell
)
file = xlsx.NewFile()
sheet, err = file.AddSheet("Sheet")
log.Printf("Added Sheet: %v", sheet)
if err != nil {
log.Printf("Err adding sheet: %v", err)
}
for i := 0; i < 50; i++ {
row = sheet.AddRow()
log.Printf("Adding row %v", row)
row.AddCell().SetString("Col A")
row.AddCell().SetString("Col B")
row.AddCell().SetString("Col C")
cell = row.AddCell()
log.Printf("Added columns: %v", cell)
cell.Value = "Loren ipsum"
}
err = file.Save(pwdDir + "/" + *saveFilePtr)
if err != nil {
log.Printf("Err saving: %v", err)
}
log.Printf("Name of file:%v", *saveFilePtr)
}
Update Produce
package main
import (
"flag"
"github.com/tealeg/xlsx"
"log"
"os"
)
func main() {
openFilePtr := flag.String("open", "produceSales.xlsx", "Excel file to open")
flag.Parse()
pwdDir, _ := os.Getwd()
fLog, err := os.OpenFile(pwdDir+`/.log`, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
log.Fatalln(err)
}
defer fLog.Close()
log.SetOutput(fLog)
excelFileName := pwdDir + "/" + *openFilePtr
log.Printf("Excel file opened: %v", *openFilePtr)
wb, err := xlsx.OpenFile(excelFileName)
if err != nil {
log.Fatalf("error opening file%v", err)
}
sheet := wb.Sheet["Sheet"]
priceUpdates := map[string]float64{
"Lemon": 4.99,
"Celery": 10.99,
"Garlic": 7.47,
}
for rowNum := 1; rowNum < sheet.MaxRow; rowNum++ {
produceName := sheet.Cell(rowNum, 0).Value
if _, ok := priceUpdates[produceName]; ok {
sheet.Cell(rowNum, 1).SetFloat(priceUpdates[produceName])
log.Printf("Row: %v Produce: %v", rowNum, produceName)
}
}
wb.Save("update" + *openFilePtr)
}
Multiplication Table
package main
import (
"flag"
"github.com/tealeg/xlsx"
"log"
"os"
"strconv"
)
func main() {
nPtr := flag.Int("number", 12, "how far to go on the table")
savePtr := flag.String("save", "multiplication_table.xlsx", "for creating a multiplication table")
flag.Parse()
pwdDir, _ := os.Getwd()
fLog, err := os.OpenFile(pwdDir+`/.log`, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
log.Fatalln(err)
}
defer fLog.Close()
log.SetOutput(fLog)
var (
file *xlsx.File
sheet *xlsx.Sheet
row *xlsx.Row
cell *xlsx.Cell
)
style := xlsx.NewStyle()
border := *xlsx.NewBorder("thin", "thin", "thin", "thin")
style.Border = border
style.ApplyBorder = true
fill := *xlsx.NewFill("solid", "00FF0000", "FF000000")
style.Fill = fill
style.ApplyFill = true
font := *xlsx.NewFont(10, "Verdana")
style.Font = font
style.ApplyFont = true
file = xlsx.NewFile()
sheet, err = file.AddSheet("Sheet")
log.Printf("Sheet Name: %v", sheet.Name)
if err != nil {
log.Printf("Error: %v", err)
}
for i := 1; i <= *nPtr; i++ {
if i == 1 {
row = sheet.AddRow()
cell = row.AddCell()
}
cell = row.AddCell()
cell.SetStyle(style)
cell.SetInt(i)
}
for i := 1; i <= *nPtr; i++ {
row = sheet.AddRow()
cell = row.AddCell()
cell.SetStyle(style)
cell.SetInt(i)
}
for i := 1; i <= *nPtr; i++ {
for j := 1; j <= *nPtr; j++ {
cell = sheet.Cell(i, j)
cell.SetInt(i * j)
log.Printf("times %v", strconv.Itoa(i)+" * "+strconv.Itoa(j))
}
}
err = file.Save(pwdDir + "/" + *savePtr)
if err != nil {
log.Printf("Error: %v", err)
}
log.Printf("save %v", *savePtr)
}
txt To XLSX
package main
import (
"bufio"
"flag"
"github.com/tealeg/xlsx"
"log"
"os"
"strings"
)
func main() {
savePtr := flag.String("save", "table.xlsx", "convert txt file to an excel file")
pwdDir, _ := os.Getwd()
fLog, err := os.OpenFile(pwdDir+`/.log`, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
log.Fatalln(err)
}
defer fLog.Close()
log.SetOutput(fLog)
file := xlsx.NewFile()
sheet, err := file.AddSheet("Sheet")
log.Printf("Sheet name %v", sheet.Name)
if err != nil {
log.Printf("Error adding sheet %v", err)
}
dir, err := os.Open(pwdDir)
if err != nil {
return
}
defer dir.Close()
fileInfos, err := dir.Readdir(-1)
if err != nil {
return
}
dataFiles := make(map[string][]string)
for _, fi := range fileInfos {
if strings.HasSuffix(fi.Name(), ".txt") {
log.Printf("File name: %v", fi.Name())
fileOpen, err := os.Open(fi.Name())
if err != nil {
log.Fatalln(err)
}
var arr []string
scanner := bufio.NewScanner(fileOpen)
for scanner.Scan() {
arr = append(arr, scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
defer fileOpen.Close()
dataFiles[fi.Name()] = arr
}
}
i := 0
for keys, prs := range dataFiles {
sheet.Cell(0, i).SetValue(keys)
l := 1
for _, str := range prs {
sheet.Cell(l, i).SetValue(str)
l++
}
i++
}
err = file.Save(pwdDir + "/" + *savePtr)
if err != nil {
log.Printf("Error: %v", err)
}
log.Printf("Saving as...%v", *savePtr)
}
XLSX to TXT
package main
import (
"flag"
"github.com/tealeg/xlsx"
"log"
"os"
"strconv"
)
var opts struct {
FileOPEN string `short:"o" long:"open" default:"table.xlsx" description:"Файл таблицы"`
}
func main() {
openPtr := flag.String("open", "table.xlsx", "file to open")
flag.Parse()
pwdDir, _ := os.Getwd()
fLog, err := os.OpenFile(pwdDir+`/.log`, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
log.Fatalln(err)
}
defer fLog.Close()
log.SetOutput(fLog)
excelFileName := pwdDir + "/" + *openPtr
log.Printf("File to open: %v", excelFileName)
wb, err := xlsx.OpenFile(excelFileName)
if err != nil {
log.Fatalf("Error: %v", err)
}
i := wb.Sheets[0]
log.Printf("Name: %v", i.Name)
for m := 0; m < i.MaxCol; m++ {
strName := "saveFile" + strconv.Itoa(m+1) + ".txt"
saveFile, err := os.Create(strName)
log.Printf("File name: %v", saveFile.Name())
if err != nil {
log.Fatalf("Error: %v", err)
}
defer saveFile.Close()
for u := 0; u < i.MaxRow; u++ {
sd := i.Cell(u, m)
ss := sd.String()
saveFile.WriteString(ss)
saveFile.WriteString("\n")
}
}
}