package sysconf import ( "bytes" "errors" "fmt" "reflect" "strconv" "strings" ) type CSV struct { header []string text [][]string } type CSVRow struct { header []string data []string } type CSVValue struct { key string value string } func ParseCSV(data []byte, hasHeader bool) (csv CSV, err error) { strData := strings.Split(string(bytes.TrimSpace(data)), "\n") if len(strData) < 1 { err = fmt.Errorf("cannot parse data,invalid data format") } var header []string var text [][]string if hasHeader { header = csvAnalyse(strData[0]) strData = strData[1:] } else { num := len(csvAnalyse(strData[0])) for i := 0; i < num; i++ { header = append(header, strconv.Itoa(i)) } } for k, v := range strData { tmpData := csvAnalyse(v) if len(tmpData) != len(header) { err = fmt.Errorf("cannot parse data line %d,got %d values but need %d", k, len(tmpData), len(header)) return } text = append(text, tmpData) } csv.header = header csv.text = text return } func (csv *CSV) Header() []string { return csv.header } func (csv *CSV) Data() [][]string { return csv.text } func (csv *CSV) Row(row int) *CSVRow { if row >= len(csv.Data()) { return nil } return &CSVRow{ header: csv.Header(), data: csv.Data()[row], } } func (csv *CSVRow) Get(key string) *CSVValue { for k, v := range csv.header { if v == key { return &CSVValue{ key: key, value: csv.data[k], } } } return nil } func (csv *CSVRow) Col(key int) *CSVValue { if key >= len(csv.header) { return nil } return &CSVValue{ key: csv.header[key], value: csv.data[key], } } func (csv *CSVRow) Header() []string { return csv.header } func (csv *CSV) MapData() []map[string]string { var result []map[string]string for _, v := range csv.text { tmp := make(map[string]string) for k, v2 := range csv.header { tmp[v2] = v[k] } result = append(result, tmp) } return result } func CsvAnalyse(data string) []string { return csvAnalyse(data) } func csvAnalyse(data string) []string { var segStart bool = false var segReady bool = false var segSign string = "" var dotReady bool = false data = strings.TrimSpace(data) var result []string var seg string for k, v := range []rune(data) { if k == 0 && v != []rune(`"`)[0] { dotReady = true } if v != []rune(`,`)[0] && dotReady { segSign = `,` segStart = true dotReady = false if v == []rune(`"`)[0] { segSign = `"` continue } } if dotReady && v == []rune(`,`)[0] { //dotReady = false result = append(result, "") continue } if v == []rune(`"`)[0] && segStart { if !segReady { segReady = true continue } seg += `"` segReady = false continue } if segReady && segSign == `"` && segStart { segReady = false segStart = false result = append(result, seg) segSign = `` seg = "" } if v == []rune(`"`)[0] && !segStart { segStart = true segReady = false segSign = `"` continue } if v == []rune(`,`)[0] && !segStart { dotReady = true } if v == []rune(`,`)[0] && segStart && segSign == "," { segStart = false result = append(result, seg) dotReady = true segSign = `` seg = "" } if segStart { seg = string(append([]rune(seg), v)) } } if len(data) != 0 && len(result) == 0 && seg == "" { result = append(result, data) } else { result = append(result, seg) } return result } func MarshalCSV(header []string, ins interface{}) ([]byte, error) { var result [][]string t := reflect.TypeOf(ins) v := reflect.ValueOf(ins) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return nil, errors.New("not a Slice or Array") } if t.Kind() == reflect.Ptr { t = t.Elem() v = v.Elem() } for i := 0; i < v.Len(); i++ { subT := reflect.TypeOf(v.Index(i).Interface()) subV := reflect.ValueOf(v.Index(i).Interface()) if subV.Kind() == reflect.Slice || subV.Kind() == reflect.Array { if subT.Kind() == reflect.Ptr { subV = subV.Elem() } var tmp []string for j := 0; j < subV.Len(); j++ { tmp = append(tmp, fmt.Sprint(reflect.ValueOf(subV.Index(j)))) } result = append(result, tmp) } if subV.Kind() == reflect.Struct { var tmp []string if subT.Kind() == reflect.Ptr { subV = subV.Elem() } for i := 0; i < subV.NumField(); i++ { tmp = append(tmp, fmt.Sprint(subV.Field(i))) } result = append(result, tmp) } } return buildCSV(header,result) } func buildCSV(header []string, data [][]string) ([]byte, error) { var result []string var length int build := func(slc []string) string { for k, v := range slc { if strings.Index(v, `"`) >= 0 { v = strings.ReplaceAll(v, `"`, `""`) } if strings.Index(v,"\n")>=0 { v=strings.ReplaceAll(v,"\n",`\n`) } if strings.Index(v,"\r")>=0 { v=strings.ReplaceAll(v,"\r",`\r`) } v = `"` + v + `"` slc[k] = v } return strings.Join(slc, ",") } if len(header) != 0 { result = append(result, build(header)) length = len(header) } else { length = len(data[0]) } for k, v := range data { if len(v) != length { return nil, fmt.Errorf("line %d got length %d ,but need %d", k, len(v), length) } result = append(result, build(v)) } return []byte(strings.Join(result, "\n")), nil }