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 Parser struct { Enclosers []EncloserId Delimiter string Fields []string Line string } func (parser *Parser) Init() { parser.Enclosers = []EncloserId{DoubleQuotes, SquareBrackets} parser.Delimiter = " \t" } func (parser *Parser) ExtractEnclosedFieldValue(endChar byte) error { lineLen := len(parser.Line) for i := 1; i < lineLen; i++ { if parser.Line[i] == endChar && parser.Line[i-1] != '\\' { parser.Fields = append(parser.Fields, parser.Line[0:i]) parser.Line = parser.Line[i+1:] return nil } } return errors.New("Encloser close not found") } /* * Usage: * import "../csv-parser/" * var parser csvparser.Parser * parser.init() * parser.Parse("a b c d") */ func (parser *Parser) Parse(CsvLine string) error { var err error = nil parser.Fields = make([]string,0) parser.Line = CsvLine for len(parser.Line) > 0 { parser.Line = strings.TrimLeft(parser.Line, parser.Delimiter) parser.Line = strings.TrimRight(parser.Line, parser.Delimiter) if len(parser.Line) == 0 { break } // Search for an encloser encloserId := None for _, id := range parser.Enclosers { if parser.Line[0] == EnclosersRunes[id].Open { encloserId = id break } } if encloserId != None { parser.Line = parser.Line[1:] err = parser.ExtractEnclosedFieldValue(EnclosersRunes[encloserId].Close) if err != nil { return err } } else { nextSpace := strings.IndexAny(parser.Line, parser.Delimiter) if nextSpace != -1 { parser.Fields = append(parser.Fields, parser.Line[:nextSpace]) parser.Line = parser.Line[nextSpace:] } else { parser.Fields = append(parser.Fields, parser.Line) parser.Line = "" break } } } return nil }