feat: detect CSV delimiter

Implements #20

/spend 5d
pull/21/head 0.3.1
Dominique Merle 3 years ago
parent 1926bb6a92
commit 7911ae91ec

@ -18,10 +18,10 @@ Hand-made builds are provided in the [Assets of each Release](https://github.com
Say you have a tally CSV like so:
, reject, poor, fair, good, very good, excellent
Pizza, 3, 2, 1, 4, 4, 2
Chips, 2, 3, 0, 4, 3, 4
Pasta, 4, 5, 1, 4, 0, 2
, reject, poor, fair, good, very good, excellent
Pizza, 3, 2, 1, 4, 4, 2
Chips, 2, 3, 0, 4, 3, 4
Pasta, 4, 5, 1, 4, 0, 2
You can run

@ -1,4 +1,4 @@
, reject, poor, fair, good, very good, excellent
Pizza, 3, 2, 1, 4, 4, 2
Chips, 2, 3, 0, 4, 3, 4
Pasta, 4, 5, 1, 4, 0, 2
, reject, poor, fair, good, very good, excellent
Pizza, 3, 2, 1, 4, 4, 2
Chips, 2, 3, 0, 4, 3, 4
Pasta, 4, 5, 1, 4, 0, 2
1 reject poor fair good very good excellent
2 Pizza 3 2 1 4 4 2
3 Chips 2 3 0 4 3 4
4 Pasta 4 5 1 4 0 2

@ -0,0 +1,3 @@
Pizza 3 2 1 4 4 2
Chips 2 3 0 4 3 4
Pasta 4 5 1 4 0 2
1 Pizza 3 2 1 4 4 2
2 Chips 2 3 0 4 3 4
3 Pasta 4 5 1 4 0 2

@ -3,6 +3,7 @@ module github.com/MieuxVoter/majority-judgment-cli
go 1.17
require (
github.com/csimplestring/go-csv v0.0.0-20180328183906-5b8b3cd94f2c
github.com/mieuxvoter/majority-judgment-library-go v0.3.1
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.9.0

@ -3,6 +3,8 @@ package reader
import (
"encoding/csv"
"errors"
"fmt"
"github.com/csimplestring/go-csv/detector"
"io"
"strings"
)
@ -16,9 +18,29 @@ func (r CsvTallyReader) Read(input *io.Reader) (
grades []string,
err error,
) {
csvDelimiter := ',' // default value if our detector below fails
csvQuote := '"'
// I. Read the whole input at once. Tried stream reading with io.Pipe but… buffer!
allData, _ := io.ReadAll(*input)
readerCloneA := strings.NewReader(string(allData))
readerCloneB := strings.NewReader(string(allData))
// I.a Detect the delimiter between values in the input (default is comma `,`)
delimiterDetector := detector.New()
delimiters := delimiterDetector.DetectDelimiter(readerCloneA, byte(csvQuote))
if 0 < len(delimiters) {
csvDelimiter = readFirstRune(delimiters[0])
}
if 1 < len(delimiters) {
err = fmt.Errorf("too many delimiters: found `%s` and `%s`", delimiters[0], delimiters[1])
return
}
// I. Read the whole input at once. Stream reading ? Perhaps someday ?
csvRows, errReader := csv.NewReader(*input).ReadAll()
// I.b Read the actual CSV contents
csvReader := csv.NewReader(readerCloneB)
csvReader.Comma = csvDelimiter
csvRows, errReader := csvReader.ReadAll()
if errReader != nil {
err = errors.New("Failed to read input CSV: " + errReader.Error())
return
@ -71,11 +93,6 @@ func (r CsvTallyReader) Read(input *io.Reader) (
}
}
// Do we still need this? Right now we should be ok.
//for gradeIndex, grade := range grades {
// grades[gradeIndex] = strings.TrimSpace(grade)
//}
return
}

@ -83,3 +83,11 @@ func GenerateDummyGradeNames(thatMany int) (grades []string, err error) {
return
}
func readFirstRune(str string) (first rune) {
for _, someRune := range str {
first = someRune
break
}
return
}

Loading…
Cancel
Save