You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
5.2 KiB
Go
262 lines
5.2 KiB
Go
4 years ago
|
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
|
||
|
}
|