diff --git a/decode_test.go b/decode_test.go index 792cc3a..8958009 100644 --- a/decode_test.go +++ b/decode_test.go @@ -1359,3 +1359,33 @@ func Test_readTo_inline_nested_struct(t *testing.T) { t.Fatalf("expected \n sample: %v\n got: %v", expected, samples) } } + +func TestUnmarshalZeroPaddedNumbers(t *testing.T) { + type row struct { + Name string `csv:"name"` + Code int `csv:"code"` + Count uint `csv:"count"` + } + + in := "name,code,count\n" + + "a,08,08\n" + + "b,09,09\n" + + "c,010,010\n" + + "d,007,007\n" + + var got []row + if err := UnmarshalString(in, &got); err != nil { + t.Fatalf("UnmarshalString failed on zero-padded numbers: %s", err) + } + + want := []row{ + {Name: "a", Code: 8, Count: 8}, + {Name: "b", Code: 9, Count: 9}, + {Name: "c", Code: 10, Count: 10}, + {Name: "d", Code: 7, Count: 7}, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("UnmarshalString returned %+v, want %+v", got, want) + } +} diff --git a/types.go b/types.go index 761208e..92d610e 100644 --- a/types.go +++ b/types.go @@ -130,7 +130,9 @@ func toInt(in interface{}) (int64, error) { return 0, nil } out := strings.SplitN(s, ".", 2) - return strconv.ParseInt(out[0], 0, 64) + // base 10, not 0: a CSV field is data, not Go source, so "010" is ten + // and "09" is nine rather than an invalid octal literal + return strconv.ParseInt(out[0], 10, 64) case reflect.Bool: if inValue.Bool() { return 1, nil @@ -164,7 +166,8 @@ func toUint(in interface{}) (uint64, error) { } return uint64(f), nil } - return strconv.ParseUint(s, 0, 64) + // base 10, see toInt + return strconv.ParseUint(s, 10, 64) case reflect.Bool: if inValue.Bool() { return 1, nil diff --git a/types_test.go b/types_test.go index 3c85c0a..335558e 100644 --- a/types_test.go +++ b/types_test.go @@ -139,3 +139,58 @@ func TestToInt(t *testing.T) { } } } + +func TestToIntLeadingZeros(t *testing.T) { + testCases := []struct { + field string + result int64 + }{ + {"08", 8}, + {"09", 9}, + {"010", 10}, + {"007", 7}, + {"0009", 9}, + {"-08", -8}, + {"-010", -10}, + {"0", 0}, + {"00", 0}, + {"010.9", 10}, + } + + for _, item := range testCases { + out, err := toInt(item.field) + if err != nil { + t.Errorf("toInt(%q) returned an error: %s", item.field, err) + continue + } + if out != item.result { + t.Errorf("toInt(%q) = %d, want %d", item.field, out, item.result) + } + } +} + +func TestToUintLeadingZeros(t *testing.T) { + testCases := []struct { + field string + result uint64 + }{ + {"08", 8}, + {"09", 9}, + {"010", 10}, + {"007", 7}, + {"0009", 9}, + {"0", 0}, + {"00", 0}, + } + + for _, item := range testCases { + out, err := toUint(item.field) + if err != nil { + t.Errorf("toUint(%q) returned an error: %s", item.field, err) + continue + } + if out != item.result { + t.Errorf("toUint(%q) = %d, want %d", item.field, out, item.result) + } + } +}