-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcsv.go
More file actions
132 lines (114 loc) · 2.7 KB
/
csv.go
File metadata and controls
132 lines (114 loc) · 2.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package gospreadsheet
import (
"encoding/csv"
"fmt"
"io"
"os"
"strconv"
)
// CSVReader reads CSV files into a Workbook.
type CSVReader struct {
Delimiter rune
LazyQuotes bool
}
// NewCSVReader creates a new CSV reader with default settings.
func NewCSVReader() *CSVReader {
return &CSVReader{
Delimiter: ',',
}
}
// Open reads a CSV file and returns a Workbook with a single worksheet.
func (r *CSVReader) Open(filename string) (*Workbook, error) {
f, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("opening file: %w", err)
}
defer f.Close()
return r.Read(f)
}
// Read reads CSV data from an io.Reader.
func (r *CSVReader) Read(reader io.Reader) (*Workbook, error) {
csvReader := csv.NewReader(reader)
csvReader.Comma = r.Delimiter
csvReader.LazyQuotes = r.LazyQuotes
csvReader.FieldsPerRecord = -1 // allow variable field count
wb := New()
ws := wb.GetActiveSheet()
row := 0
for {
record, err := csvReader.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("reading CSV row %d: %w", row+1, err)
}
for col, val := range record {
cell := ws.GetCell(row, col)
// Try to detect numeric values
if v, err := strconv.ParseFloat(val, 64); err == nil {
cell.SetValue(v)
} else if v, err := strconv.ParseBool(val); err == nil {
cell.SetValue(v)
} else {
cell.SetValue(val)
}
}
row++
}
return wb, nil
}
// CSVWriter writes a Workbook to CSV format.
type CSVWriter struct {
Delimiter rune
SheetIndex int // which sheet to write (0-based)
}
// NewCSVWriter creates a new CSV writer with default settings.
func NewCSVWriter() *CSVWriter {
return &CSVWriter{
Delimiter: ',',
}
}
// Save writes the workbook to a CSV file.
func (w *CSVWriter) Save(wb *Workbook, filename string) error {
f, err := os.Create(filename)
if err != nil {
return fmt.Errorf("creating file: %w", err)
}
writeErr := w.Write(wb, f)
closeErr := f.Close()
if writeErr != nil {
return writeErr
}
if closeErr != nil {
return fmt.Errorf("closing file: %w", closeErr)
}
return nil
}
// Write writes the workbook to an io.Writer.
func (w *CSVWriter) Write(wb *Workbook, writer io.Writer) error {
ws, err := wb.GetSheet(w.SheetIndex)
if err != nil {
return err
}
csvWriter := csv.NewWriter(writer)
csvWriter.Comma = w.Delimiter
defer csvWriter.Flush()
rows, err := ws.RowIterator()
if err != nil {
// Empty sheet, write nothing
return nil
}
for _, row := range rows {
record := make([]string, len(row))
for i, cell := range row {
if cell != nil {
record[i] = cell.GetStringValue()
}
}
if err := csvWriter.Write(record); err != nil {
return fmt.Errorf("writing CSV row: %w", err)
}
}
return csvWriter.Error()
}