2021-05-17 15:09:04 +08:00
|
|
|
package sysconf
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
2026-06-09 18:10:19 +08:00
|
|
|
"encoding/csv"
|
2021-05-17 15:09:04 +08:00
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
var ErrNilCSVValue = errors.New("nil csv value")
|
|
|
|
|
|
2021-05-17 15:09:04 +08:00
|
|
|
type CSV struct {
|
|
|
|
|
header []string
|
|
|
|
|
text [][]string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type CSVRow struct {
|
|
|
|
|
header []string
|
|
|
|
|
data []string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type CSVValue struct {
|
|
|
|
|
key string
|
|
|
|
|
value string
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
func ParseCSV(data []byte, hasHeader bool) (csvData CSV, err error) {
|
|
|
|
|
if len(data) == 0 {
|
|
|
|
|
return CSV{}, fmt.Errorf("cannot parse data,invalid data format")
|
|
|
|
|
}
|
|
|
|
|
reader := csv.NewReader(bytes.NewReader(data))
|
|
|
|
|
records, err := reader.ReadAll()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return CSV{}, err
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
if len(records) == 0 {
|
|
|
|
|
return CSV{}, fmt.Errorf("cannot parse data,invalid data format")
|
|
|
|
|
}
|
|
|
|
|
start := 0
|
2021-05-17 15:09:04 +08:00
|
|
|
if hasHeader {
|
2026-06-09 18:10:19 +08:00
|
|
|
csvData.header = append([]string(nil), records[0]...)
|
|
|
|
|
start = 1
|
2021-05-17 15:09:04 +08:00
|
|
|
} else {
|
2026-06-09 18:10:19 +08:00
|
|
|
for i := range records[0] {
|
|
|
|
|
csvData.header = append(csvData.header, fmt.Sprint(i))
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
for _, record := range records[start:] {
|
|
|
|
|
if len(record) != len(csvData.header) {
|
|
|
|
|
return CSV{}, fmt.Errorf("cannot parse data line,got %d values but need %d", len(record), len(csvData.header))
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
csvData.text = append(csvData.text, append([]string(nil), record...))
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
return csvData, nil
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
func (csvData *CSV) Header() []string { return csvData.header }
|
|
|
|
|
func (csvData *CSV) Data() [][]string { return csvData.text }
|
2021-05-17 15:09:04 +08:00
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
func (csvData *CSV) Row(row int) *CSVRow {
|
|
|
|
|
if csvData == nil || row < 0 || row >= len(csvData.text) {
|
2021-05-17 15:09:04 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
return &CSVRow{header: csvData.header, data: csvData.text[row]}
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
func (row *CSVRow) Get(key string) *CSVValue {
|
|
|
|
|
if row == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
for idx, header := range row.header {
|
|
|
|
|
if header == key {
|
|
|
|
|
return &CSVValue{key: key, value: row.data[idx]}
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
func (row *CSVRow) Col(key int) *CSVValue {
|
|
|
|
|
if row == nil || key < 0 || key >= len(row.header) {
|
2021-05-17 15:09:04 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
return &CSVValue{key: row.header[key], value: row.data[key]}
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
func (row *CSVRow) Header() []string { return row.header }
|
2021-05-17 15:09:04 +08:00
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
func (csvData *CSV) MapData() []map[string]string {
|
2021-05-17 15:09:04 +08:00
|
|
|
var result []map[string]string
|
2026-06-09 18:10:19 +08:00
|
|
|
for _, record := range csvData.text {
|
|
|
|
|
item := make(map[string]string, len(csvData.header))
|
|
|
|
|
for idx, header := range csvData.header {
|
|
|
|
|
item[header] = record[idx]
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
result = append(result, item)
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 18:10:19 +08:00
|
|
|
func CsvAnalyse(data string) []string { return csvAnalyse(data) }
|
2021-05-17 15:09:04 +08:00
|
|
|
|
|
|
|
|
func csvAnalyse(data string) []string {
|
2026-06-09 18:10:19 +08:00
|
|
|
reader := csv.NewReader(strings.NewReader(data))
|
|
|
|
|
record, err := reader.Read()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return []string{}
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
return record
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func MarshalCSV(header []string, ins interface{}) ([]byte, error) {
|
|
|
|
|
v := reflect.ValueOf(ins)
|
2026-06-09 18:10:19 +08:00
|
|
|
if v.Kind() == reflect.Ptr {
|
|
|
|
|
if v.IsNil() {
|
|
|
|
|
return nil, ErrNilCSVValue
|
|
|
|
|
}
|
2021-05-17 15:09:04 +08:00
|
|
|
v = v.Elem()
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
|
|
|
|
return nil, fmt.Errorf("not a Slice or Array")
|
|
|
|
|
}
|
|
|
|
|
rows := make([][]string, 0, v.Len())
|
2021-05-17 15:09:04 +08:00
|
|
|
for i := 0; i < v.Len(); i++ {
|
2026-06-09 18:10:19 +08:00
|
|
|
item := v.Index(i)
|
|
|
|
|
if item.Kind() == reflect.Ptr {
|
|
|
|
|
if item.IsNil() {
|
|
|
|
|
continue
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
item = item.Elem()
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
switch item.Kind() {
|
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
|
row := make([]string, 0, item.Len())
|
|
|
|
|
for j := 0; j < item.Len(); j++ {
|
|
|
|
|
row = append(row, fmt.Sprint(item.Index(j).Interface()))
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
rows = append(rows, row)
|
|
|
|
|
case reflect.Struct:
|
|
|
|
|
row := make([]string, 0, item.NumField())
|
|
|
|
|
for j := 0; j < item.NumField(); j++ {
|
|
|
|
|
field := item.Field(j)
|
|
|
|
|
if !field.CanInterface() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
row = append(row, fmt.Sprint(field.Interface()))
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
rows = append(rows, row)
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
width := 0
|
|
|
|
|
if len(header) > 0 {
|
|
|
|
|
width = len(header)
|
|
|
|
|
} else if len(rows) > 0 {
|
|
|
|
|
width = len(rows[0])
|
|
|
|
|
}
|
|
|
|
|
for idx, row := range rows {
|
|
|
|
|
if len(row) != width {
|
|
|
|
|
return nil, fmt.Errorf("line %d got length %d ,but need %d", idx, len(row), width)
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
var buf bytes.Buffer
|
|
|
|
|
writer := csv.NewWriter(&buf)
|
|
|
|
|
if len(header) > 0 {
|
|
|
|
|
if err := writer.Write(header); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
for _, row := range rows {
|
|
|
|
|
if err := writer.Write(row); err != nil {
|
|
|
|
|
return nil, err
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
writer.Flush()
|
|
|
|
|
return buf.Bytes(), writer.Error()
|
2021-05-17 15:09:04 +08:00
|
|
|
}
|