diff --git a/csv_files/valid_bom.csv b/csv_files/valid_bom.csv new file mode 100644 index 0000000..4182d52 --- /dev/null +++ b/csv_files/valid_bom.csv @@ -0,0 +1,4 @@ +header1, header2, header3 +line1, 1, 1.2 +line2, 2, 2.3 +line3, 3, 3.4 diff --git a/load.go b/load.go index 20bb159..f48ffd6 100644 --- a/load.go +++ b/load.go @@ -1,6 +1,7 @@ package csvtag import ( + "bytes" "encoding/csv" "fmt" "io" @@ -113,6 +114,8 @@ func LoadFromString(str string, destination interface{}, options ...CsvOptions) // @param separator: the separator used in the csv file. // @param header: the optional header if the file does not contain one. func readFile(file io.Reader, separator rune, header []string) ([]string, [][]string, error) { + file = skipBOM(file) + // Create and configure the csv reader. reader := csv.NewReader(file) reader.TrimLeadingSpace = true @@ -141,6 +144,24 @@ func readFile(file io.Reader, separator rune, header []string) ([]string, [][]st return header, content, nil } +// Skip the Byte Order Mark (BOM) if it exists. +// @param file: the io.Reader to read from. +func skipBOM(file io.Reader) io.Reader { + // Read the first 3 bytes. + bom := make([]byte, 3) + _, err := file.Read(bom) + if err != nil { + return file + } + + // If the first 3 bytes are not the BOM, reset the reader. + if bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF { + return io.MultiReader(bytes.NewReader(bom), file) + } + + return file +} + // Map the provided content to the destination using the header and the tags. // @param header: the csv header to match with the struct's tags. // @param content: the content to put in destination. diff --git a/load_test.go b/load_test.go index b147e43..6abc4fc 100644 --- a/load_test.go +++ b/load_test.go @@ -55,6 +55,14 @@ func TestValideFile(t *testing.T) { } } +func TestValideFileWithBOM(t *testing.T) { + tabT := []test{} + err := LoadFromPath("csv_files/valid_bom.csv", &tabT) + if err != nil || checkValues(tabT) { + t.Fail() + } +} + func TestBool(t *testing.T) { tabT := []testBool{} err := LoadFromPath("csv_files/bool.csv", &tabT)