From fd5cf2c1f6be6c44e4d2b86812bb72aa1408ebd8 Mon Sep 17 00:00:00 2001 From: Starainrt Date: Mon, 17 May 2021 15:09:04 +0800 Subject: [PATCH] csv support --- files_unix.go | 2 +- files_windows.go | 8 ++ sysconf/csv.go | 261 ++++++++++++++++++++++++++++++++++++++++ sysconf/csv_test.go | 38 ++++++ sysconf/csvconvert.go | 120 ++++++++++++++++++ sysconf/sysconf.go | 22 ++-- sysconf/sysconf_test.go | 30 +++++ 7 files changed, 472 insertions(+), 9 deletions(-) create mode 100644 sysconf/csv.go create mode 100644 sysconf/csv_test.go create mode 100644 sysconf/csvconvert.go diff --git a/files_unix.go b/files_unix.go index 013182c..beb6b8c 100644 --- a/files_unix.go +++ b/files_unix.go @@ -18,4 +18,4 @@ func GetFileCreationTime(fileinfo os.FileInfo) time.Time { func GetFileAccessTime(fileinfo os.FileInfo) time.Time { return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atim) -} +} \ No newline at end of file diff --git a/files_windows.go b/files_windows.go index 8ea3f27..63fb604 100644 --- a/files_windows.go +++ b/files_windows.go @@ -17,3 +17,11 @@ func GetFileAccessTime(fileinfo os.FileInfo) time.Time { d := fileinfo.Sys().(*syscall.Win32FileAttributeData) return time.Unix(0, d.LastAccessTime.Nanoseconds()) } + +func SetFileTimes(file *os.File,info os.FileInfo) { + +} + +func SetFileTimesbyTime(file *os.File) { + syscall.SetFileTime() +} \ No newline at end of file diff --git a/sysconf/csv.go b/sysconf/csv.go new file mode 100644 index 0000000..0980456 --- /dev/null +++ b/sysconf/csv.go @@ -0,0 +1,261 @@ +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 +} diff --git a/sysconf/csv_test.go b/sysconf/csv_test.go new file mode 100644 index 0000000..799e8ba --- /dev/null +++ b/sysconf/csv_test.go @@ -0,0 +1,38 @@ +package sysconf + +import ( + "fmt" + "testing" +) + +func Test_csv(t *testing.T) { + //var test Sqlplus + var text=` +姓名,班级,性别,年龄 +张三,"我,不""知道",boy,23 +"里斯","哈哈",girl,23 +` + fmt.Println(csvAnalyse(`请求权,lkjdshck,dsvdsv,"sdvkjsdv,",=dsvdsv,"=,dsvsdv"`)) + a,b:=ParseCSV([]byte(text),true) + fmt.Println(b) + fmt.Println(a.Row(0).Col(3).MustInt()) +} + +type csvtest struct { + A string + B int +} +func Test_Masharl(t *testing.T) { + //var test Sqlplus + /* + var a []csvtest = []csvtest{ + {"lala",1}, + {"haha",34}, + } + */ + var a [][]string + a=append(a,[]string{"a","b","c"}) + a=append(a,[]string{"1",`s"s"d`,"3"}) + b,_:=MarshalCSV([]string{},a) + fmt.Println(string(b)) +} diff --git a/sysconf/csvconvert.go b/sysconf/csvconvert.go new file mode 100644 index 0000000..54979ef --- /dev/null +++ b/sysconf/csvconvert.go @@ -0,0 +1,120 @@ +package sysconf + +import "strconv" + +func (csv *CSVValue)Key()string { + return csv.key +} + +func (csv *CSVValue)Int()(int,error) { + tmp,err:=strconv.Atoi(csv.value) + return tmp,err +} + +func (csv *CSVValue)MustInt()int { + tmp,err:=csv.Int() + if err!=nil { + panic(err) + } + return tmp +} + +func (csv *CSVValue)Int64()(int64,error) { + tmp,err:=strconv.ParseInt(csv.value,10,64) + return tmp,err +} + +func (csv *CSVValue)MustInt64()int64 { + tmp,err:=csv.Int64() + if err!=nil { + panic(err) + } + return tmp +} + +func (csv *CSVValue)Int32()(int32,error) { + tmp,err:=strconv.ParseInt(csv.value,10,32) + return int32(tmp),err +} + +func (csv *CSVValue)MustInt32()int32 { + tmp,err:=csv.Int32() + if err!=nil { + panic(err) + } + return tmp +} + +func (csv *CSVValue)Uint64()(uint64,error) { + tmp,err:=strconv.ParseUint(csv.value,10,64) + return tmp,err +} + +func (csv *CSVValue)MustUint64()uint64 { + tmp,err:=csv.Uint64() + if err!=nil { + panic(err) + } + return tmp +} + +func (csv *CSVValue)Uint32()(uint32,error) { + tmp,err:=strconv.ParseUint(csv.value,10,32) + return uint32(tmp),err +} + +func (csv *CSVValue)MustUint32()uint32 { + tmp,err:=csv.Uint32() + if err!=nil { + panic(err) + } + return tmp +} + +func (csv *CSVValue)String()string { + return csv.value +} + +func (csv *CSVValue)Byte()[]byte { + return []byte(csv.value) +} + + +func (csv *CSVValue)Bool()(bool,error) { + tmp,err:=strconv.ParseBool(csv.value) + return tmp,err +} + +func (csv *CSVValue)MustBool()bool { + tmp,err:=csv.Bool() + if err!=nil { + panic(err) + } + return tmp +} + +func (csv *CSVValue)Float64()(float64,error) { + tmp,err:=strconv.ParseFloat(csv.value,64) + return tmp,err +} + +func (csv *CSVValue)MustFloat64()float64 { + tmp,err:=csv.Float64() + if err!=nil { + panic(err) + } + return tmp +} + +func (csv *CSVValue)Float32()(float32,error) { + tmp,err:=strconv.ParseFloat(csv.value,32) + return float32(tmp),err +} + +func (csv *CSVValue)MustFloat32()float32 { + tmp,err:=csv.Float32() + if err!=nil { + panic(err) + } + return tmp +} \ No newline at end of file diff --git a/sysconf/sysconf.go b/sysconf/sysconf.go index bc015c9..076856e 100644 --- a/sysconf/sysconf.go +++ b/sysconf/sysconf.go @@ -482,7 +482,7 @@ func SliceIn(slice interface{}, data interface{}) bool { // Unmarshal 输出结果到结构体中 func (cfg *SysConf) Unmarshal(ins interface{}) error { - var structSet func(t reflect.Type, v reflect.Value) error + var structSet func(t reflect.Type, v reflect.Value,oriSeg string) error t := reflect.TypeOf(ins) v := reflect.ValueOf(ins).Elem() if v.Kind() != reflect.Struct { @@ -492,7 +492,7 @@ func (cfg *SysConf) Unmarshal(ins interface{}) error { return errors.New("Cannot Write!") } t = t.Elem() - structSet = func(t reflect.Type, v reflect.Value) error { + structSet = func(t reflect.Type, v reflect.Value,oriSeg string) error { for i := 0; i < t.NumField(); i++ { tp := t.Field(i) vl := v.Field(i) @@ -500,11 +500,14 @@ func (cfg *SysConf) Unmarshal(ins interface{}) error { continue } if vl.Type().Kind() == reflect.Struct { - structSet(vl.Type(), vl) + structSet(vl.Type(), vl,tp.Tag.Get("seg")) continue } seg := tp.Tag.Get("seg") key := tp.Tag.Get("key") + if oriSeg!="" { + seg=oriSeg + } if seg == "" || key == "" { continue } @@ -532,12 +535,12 @@ func (cfg *SysConf) Unmarshal(ins interface{}) error { } return nil } - return structSet(t, v) + return structSet(t, v,"") } // Marshal 输出结果到结构体中 func (cfg *SysConf) Marshal(ins interface{}) ([]byte, error) { - var structSet func(t reflect.Type, v reflect.Value) + var structSet func(t reflect.Type, v reflect.Value,oriSeg string) t := reflect.TypeOf(ins) v := reflect.ValueOf(ins) if v.Kind() != reflect.Struct { @@ -547,18 +550,21 @@ func (cfg *SysConf) Marshal(ins interface{}) ([]byte, error) { t = t.Elem() v = v.Elem() } - structSet = func(t reflect.Type, v reflect.Value) { + structSet = func(t reflect.Type, v reflect.Value,oriSeg string) { for i := 0; i < t.NumField(); i++ { var seg, key, comment string = "", "", "" tp := t.Field(i) vl := v.Field(i) if vl.Type().Kind() == reflect.Struct { - structSet(vl.Type(), vl) + structSet(vl.Type(), vl,tp.Tag.Get("seg")) continue } seg = tp.Tag.Get("seg") key = tp.Tag.Get("key") comment = tp.Tag.Get("comment") + if oriSeg != "" { + seg=oriSeg + } if seg == "" || key == "" { continue } @@ -569,7 +575,7 @@ func (cfg *SysConf) Marshal(ins interface{}) ([]byte, error) { } } - structSet(t, v) + structSet(t, v,"") return cfg.Build(), nil } diff --git a/sysconf/sysconf_test.go b/sysconf/sysconf_test.go index 54d3920..aa0e48c 100644 --- a/sysconf/sysconf_test.go +++ b/sysconf/sysconf_test.go @@ -36,3 +36,33 @@ func Test_Parse(t *testing.T) { //fmt.Println(cfg.Data[0].Comment) fmt.Println(string(cfg.Build())) } + + +type slicetest struct { + A string `seg:"s" key:"a"` + B string `seg:"a" key:"b"` +} + +type testme struct { + Love slicetest `seg:"love"` + Star slicetest `seg:"star"` +} + +func Test_Marshal(t *testing.T) { + + var info string =` +[love] +a=abc +b=123 +[star] +a=456 +b=789 +` + var tmp testme + ini:=NewIni() + ini.Parse([]byte(info)) + ini.Unmarshal(&tmp) + fmt.Printf("%+v\n",tmp) + b,_:=ini.Marshal(tmp) + fmt.Println(string(b)) +} \ No newline at end of file