Skip to content

Commit 4e98c21

Browse files
committed
Read table from file is added.
1 parent 3a7b22d commit 4e98c21

File tree

3 files changed

+160
-41
lines changed

3 files changed

+160
-41
lines changed

README.md

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[![GoDoc](https://godoc.org/github.com/cinar/csv2?status.svg)](https://godoc.org/github.com/cinar/csv2)
22
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
3-
[![Build Status](https://travis-ci.com/cinar/csv2.svg?branch=master)](https://travis-ci.com/cinar/csv2)
3+
[![Build Status](https://travis-ci.com/cinar/csv2.svg?branch=main)](https://travis-ci.com/cinar/csv2)
44

55
# Csv2 Go
66

@@ -68,11 +68,50 @@ var prices []dailyPrice
6868
Use the [ReadRowsFromFile](https://pkg.go.dev/github.com/cinar/csv2#ReadRowsFromFile) function to read the CSV file into the slice.
6969

7070
```Golang
71-
err := ReadRowsFromFile(testFile, true, &prices)
71+
err := csv2.ReadRowsFromFile(testFile, true, &prices)
7272
if err != nil {
7373
return err
7474
}
7575
```
76+
77+
### Reading as a table
78+
79+
Define a structure for the table.
80+
81+
```Golang
82+
// Stock prices structure for all columns.
83+
type stockPrices struct {
84+
Date []time.Time `format:"2006-01-02 15:04:05-07:00"`
85+
Close []float64
86+
High []float64
87+
Low []float64
88+
Open []float64
89+
Volume []int64
90+
AdjClose []float64
91+
AdjHigh []float64
92+
AdjLow []float64
93+
AdjOpen []float64
94+
AdjVolume []int64
95+
DivCash []float64
96+
SplitFactor []float64
97+
}
98+
```
99+
100+
Define an instance of the table structure.
101+
102+
```Golang
103+
prices := stockPrices{}
104+
```
105+
106+
Use the [ReadTableFromFile](https://pkg.go.dev/github.com/cinar/csv2#ReadRowsFromFile) function to read the CSV file into the table.
107+
108+
```Golang
109+
err := csv2.ReadTableFromFile(testFile, true, &prices)
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
```
114+
76115
## License
77116

78117
The source code is provided under MIT License.

csv2.go

+105-38
Original file line numberDiff line numberDiff line change
@@ -32,111 +32,111 @@ type columnInfo struct {
3232
Format string
3333
}
3434

35-
func setBoolFieldValue(fieldValue reflect.Value, stringValue string) error {
36-
value, err := strconv.ParseBool(stringValue)
35+
func setBoolValue(value reflect.Value, stringValue string) error {
36+
actualValue, err := strconv.ParseBool(stringValue)
3737
if err == nil {
38-
fieldValue.SetBool(value)
38+
value.SetBool(actualValue)
3939
}
4040

4141
return err
4242
}
4343

44-
func setIntFieldValue(fieldValue reflect.Value, stringValue string, bitSize int) error {
45-
value, err := strconv.ParseInt(stringValue, 10, bitSize)
44+
func setIntValue(value reflect.Value, stringValue string, bitSize int) error {
45+
actualValue, err := strconv.ParseInt(stringValue, 10, bitSize)
4646
if err == nil {
47-
fieldValue.SetInt(value)
47+
value.SetInt(actualValue)
4848
}
4949

5050
return err
5151
}
5252

53-
func setUintFieldValue(fieldValue reflect.Value, stringValue string, bitSize int) error {
54-
value, err := strconv.ParseUint(stringValue, 10, bitSize)
53+
func setUintValue(value reflect.Value, stringValue string, bitSize int) error {
54+
actualValue, err := strconv.ParseUint(stringValue, 10, bitSize)
5555
if err == nil {
56-
fieldValue.SetUint(value)
56+
value.SetUint(actualValue)
5757
}
5858

5959
return err
6060
}
6161

62-
func setFloatFieldValue(fieldValue reflect.Value, stringValue string, bitSize int) error {
63-
value, err := strconv.ParseFloat(stringValue, bitSize)
62+
func setFloatValue(value reflect.Value, stringValue string, bitSize int) error {
63+
actualValue, err := strconv.ParseFloat(stringValue, bitSize)
6464
if err == nil {
65-
fieldValue.SetFloat(value)
65+
value.SetFloat(actualValue)
6666
}
6767

6868
return err
6969
}
7070

71-
func setTimeFieldValue(fieldValue reflect.Value, stringValue string, format string) error {
72-
value, err := time.Parse(format, stringValue)
71+
func setTimeValue(value reflect.Value, stringValue string, format string) error {
72+
actualValue, err := time.Parse(format, stringValue)
7373
if err == nil {
74-
fieldValue.Set(reflect.ValueOf(value))
74+
value.Set(reflect.ValueOf(actualValue))
7575
}
7676

7777
return err
7878
}
7979

80-
func setFieldValue(fieldValue reflect.Value, stringValue string, format string) error {
81-
fieldKind := fieldValue.Kind()
80+
func setValue(value reflect.Value, stringValue string, format string) error {
81+
kind := value.Kind()
8282

83-
switch fieldKind {
83+
switch kind {
8484
case reflect.String:
85-
fieldValue.SetString(stringValue)
85+
value.SetString(stringValue)
8686
return nil
8787

8888
case reflect.Bool:
89-
return setBoolFieldValue(fieldValue, stringValue)
89+
return setBoolValue(value, stringValue)
9090

9191
case reflect.Int:
92-
return setIntFieldValue(fieldValue, stringValue, bits.UintSize)
92+
return setIntValue(value, stringValue, bits.UintSize)
9393

9494
case reflect.Int8:
95-
return setIntFieldValue(fieldValue, stringValue, 8)
95+
return setIntValue(value, stringValue, 8)
9696

9797
case reflect.Int16:
98-
return setIntFieldValue(fieldValue, stringValue, 16)
98+
return setIntValue(value, stringValue, 16)
9999

100100
case reflect.Int32:
101-
return setIntFieldValue(fieldValue, stringValue, 32)
101+
return setIntValue(value, stringValue, 32)
102102

103103
case reflect.Int64:
104-
return setIntFieldValue(fieldValue, stringValue, 64)
104+
return setIntValue(value, stringValue, 64)
105105

106106
case reflect.Uint:
107-
return setUintFieldValue(fieldValue, stringValue, bits.UintSize)
107+
return setUintValue(value, stringValue, bits.UintSize)
108108

109109
case reflect.Uint8:
110-
return setUintFieldValue(fieldValue, stringValue, 8)
110+
return setUintValue(value, stringValue, 8)
111111

112112
case reflect.Uint16:
113-
return setUintFieldValue(fieldValue, stringValue, 16)
113+
return setUintValue(value, stringValue, 16)
114114

115115
case reflect.Uint32:
116-
return setUintFieldValue(fieldValue, stringValue, 32)
116+
return setUintValue(value, stringValue, 32)
117117

118118
case reflect.Uint64:
119-
return setUintFieldValue(fieldValue, stringValue, 64)
119+
return setUintValue(value, stringValue, 64)
120120

121121
case reflect.Float32:
122-
return setFloatFieldValue(fieldValue, stringValue, 32)
122+
return setFloatValue(value, stringValue, 32)
123123

124124
case reflect.Float64:
125-
return setFloatFieldValue(fieldValue, stringValue, 64)
125+
return setFloatValue(value, stringValue, 64)
126126

127127
case reflect.Struct:
128-
fieldTypeString := fieldValue.Type().String()
128+
typeString := value.Type().String()
129129

130-
switch fieldTypeString {
130+
switch typeString {
131131
case "time.Time":
132-
return setTimeFieldValue(fieldValue, stringValue, format)
132+
return setTimeValue(value, stringValue, format)
133133

134134
default:
135-
return fmt.Errorf("unsupported struct type %s", fieldTypeString)
135+
return fmt.Errorf("unsupported struct type %s", typeString)
136136
}
137137

138138
default:
139-
return fmt.Errorf("unsupported field kind %s", fieldKind)
139+
return fmt.Errorf("unsupported value kind %s", kind)
140140
}
141141
}
142142

@@ -227,7 +227,7 @@ func ReadRowsFromReader(reader io.Reader, hasHeader bool, rows interface{}) erro
227227
row := reflect.New(rowType).Elem()
228228

229229
for _, column := range columns {
230-
if err = setFieldValue(row.Field(column.FieldIndex), record[column.ColumnIndex], column.Format); err != nil {
230+
if err = setValue(row.Field(column.FieldIndex), record[column.ColumnIndex], column.Format); err != nil {
231231
return err
232232
}
233233
}
@@ -251,3 +251,70 @@ func ReadRowsFromFile(fileName string, hasHeader bool, rows interface{}) error {
251251

252252
return ReadRowsFromReader(file, hasHeader, rows)
253253
}
254+
255+
// Read table from reader.
256+
func ReadTableFromReader(reader io.Reader, hasHeader bool, table interface{}) error {
257+
tablePtrType := reflect.TypeOf(table)
258+
if tablePtrType.Kind() != reflect.Ptr {
259+
return errors.New("table not a pointer")
260+
}
261+
262+
tableType := tablePtrType.Elem()
263+
if tableType.Kind() != reflect.Struct {
264+
return errors.New("table not a pointer to struct")
265+
}
266+
267+
for i := 0; i < tableType.NumField(); i++ {
268+
if tableType.Field(i).Type.Kind() != reflect.Slice {
269+
return errors.New("table fields must be all slices")
270+
}
271+
}
272+
273+
tableValue := reflect.ValueOf(table).Elem()
274+
275+
columns := getStructFieldsAsColumns(tableType)
276+
277+
csvReader := csv.NewReader(reader)
278+
279+
if hasHeader {
280+
if err := readHeader(*csvReader, columns); err != nil {
281+
return err
282+
}
283+
}
284+
285+
for {
286+
record, err := csvReader.Read()
287+
if err == io.EOF {
288+
break
289+
}
290+
291+
if err != nil {
292+
return err
293+
}
294+
295+
for _, column := range columns {
296+
sliceValue := tableValue.Field(column.FieldIndex)
297+
298+
itemValue := reflect.New(sliceValue.Type().Elem()).Elem()
299+
if err = setValue(itemValue, record[column.ColumnIndex], column.Format); err != nil {
300+
return err
301+
}
302+
303+
sliceValue.Set(reflect.Append(sliceValue, itemValue))
304+
}
305+
}
306+
307+
return nil
308+
}
309+
310+
// Read table from file.
311+
func ReadTableFromFile(fileName string, hasHeader bool, rows interface{}) error {
312+
file, err := os.Open(fileName)
313+
if err != nil {
314+
return err
315+
}
316+
317+
defer file.Close()
318+
319+
return ReadTableFromReader(file, hasHeader, rows)
320+
}

csv2_test.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type dailyPrice struct {
3030

3131
// Stock prices structure for all columns.
3232
type stockPrices struct {
33-
Date []time.Time
33+
Date []time.Time `format:"2006-01-02 15:04:05-07:00"`
3434
Close []float64
3535
High []float64
3636
Low []float64
@@ -57,3 +57,16 @@ func TestReadRowsFromFile(t *testing.T) {
5757
t.Fatalf("prices must have 10 element but has %d", n)
5858
}
5959
}
60+
61+
func TestReadTableFromFile(t *testing.T) {
62+
prices := stockPrices{}
63+
64+
err := ReadTableFromFile(testFile, true, &prices)
65+
if err != nil {
66+
t.Fatal(err)
67+
}
68+
69+
if n := len(prices.Date); n != 10 {
70+
t.Fatalf("date must have 10 elements but has %d", n)
71+
}
72+
}

0 commit comments

Comments
 (0)