package csvparser import ( "strings" "errors" ) type EncloserId int const ( None EncloserId = iota DoubleQuotes SingleQuotes RoundBrackets SquareBrackets CurlyBrackets ) type Encloser struct { Open byte Close byte } var EnclosersRunes = map[EncloserId]Encloser{ DoubleQuotes: Encloser{'"', '"'}, SingleQuotes: Encloser{'\'', '\''}, RoundBrackets: Encloser{'(', ')'}, SquareBrackets: Encloser{'[', ']'}, CurlyBrackets: Encloser{'{', '}'}, } type CsvParser struct { Enclosers []EncloserId Delimiter string Fields []string Line string } func (parser *CsvParser) Init() { parser.Enclosers = []EncloserId{DoubleQuotes, SquareBrackets} parser.Delimiter = " \t" } func (parser *CsvParser) ExtractEnclosedFieldValue(line string, endChar byte) (string, error) { lineLen := len(line) for i := 1; i < lineLen; i++ { if line[i] == endChar && line[i-1] != '\\' { parser.Fields = append(parser.Fields, line[1:i]) return line[i+1:], nil } } return line, errors.New("Encloser close not found") } func (parser *CsvParser) Parse(line string) error { var err error = nil for len(line) > 0 { line = strings.TrimLeft(line, parser.Delimiter) line = strings.TrimRight(line, parser.Delimiter) if len(line) == 0 { break } // Search for an encloser encloserId := None for _, id := range parser.Enclosers { if line[0] == EnclosersRunes[id].Open { encloserId = id break } } if encloserId != None { line, err = parser.ExtractEnclosedFieldValue(line, EnclosersRunes[encloserId].Close) if err != nil { return err } } else { nextSpace := strings.IndexAny(line, parser.Delimiter) if nextSpace != -1 { parser.Fields = append(parser.Fields, line[:nextSpace]) line = line[nextSpace:] } else { parser.Fields = append(parser.Fields, line) line = "" break } } } return nil }