package main import ( "encoding/csv" "fmt" "github.com/axgle/mahonia" "io" "io/ioutil" "log" "os" "path" "pro2d/csvdata" "reflect" "strconv" "strings" "unicode" ) /** * 将excel中的前四列转化为struct * 第一列字段类型 如 int * 第二列字段名称 如 显示顺序 * 第三列字段名 如 id * 第四列s,c,all s表示服务端使用 c表示客户端使用 all表示都使用 */ var ( lineNumber = 4 // 每个工作表至少需要读取的行数 structBegin = "type %s struct {\n" // 结构体开始 structValue = " %s %s `json:\"%s\" client:\"%s\"`" // 结构体的内容 structValueForServer = " %s %s `json:\"%s\"`" // 服务端使用的结构体内容 structRemarks = " // %s" // 结构体备注 structValueEnd = "\n" // 结构体内容结束 structEnd = "}\n" // 结构体结束 header = "package %s\n\r" // 文件头 initpkg ="package csvdata\n\nimport \"sync\"\n\nvar CsvStruct map[string]interface{}\nvar CsvData *sync.Map\n\nfunc init() {\n\tCsvStruct = make(map[string]interface{})\n\tCsvData = new(sync.Map)\n\n\t%s\n}\n\nfunc GetCsv(key string) interface{}{\n\tif data, ok := CsvData.Load(key); ok {\n\t\treturn data\n\t}\n\treturn nil\n}\nfunc SetCsv(key, value interface{}) {\n\tCsvData.Store(key, value)\n}" initline = "CsvStruct[\"%s\"] = %s{}\n" ) type Generate struct { savePath string // 生成文件的保存路径 data string // 生成文件的内容 allType string // 文件当中的数据类型 } func NewGenerate(savePath, allType string) *Generate { return &Generate{ savePath: savePath, allType: allType, } } // GBK 转 UTF-8 func GbkToUtf8(s string) string { decoder := mahonia.NewDecoder("GBK") return decoder.ConvertString(s) } // UTF-8 转 GBK func Utf8ToGbk(s string) string { decoder := mahonia.NewDecoder("UTF-8") return decoder.ConvertString(s) } func GetGoTByCustomerT(t string) string { switch t { case "IntSlice": return "[]int" case "StringSlice": return "[]string" case "FloatSlice": return "[]float32" default: return t } } func CustomerTypeToGo(allType string) map[string]bool { s := strings.Split(allType, ",") mt := make(map[string]bool) for _, t := range s { mt[GetGoTByCustomerT(t)] = true } return mt } //csv to struct func (g *Generate) CsvToStruct(csvpath, filename string) error { csvfile, err := os.Open(csvpath+filename) if err != nil { return fmt.Errorf("ReadExcel|xlsx.OpenFile is err :%v", err) } defer csvfile.Close() r := csv.NewReader(csvfile) i := 0 sheetData := make([][]string, 0) for { if i >= lineNumber { break } record, err := r.Read() if err == io.EOF { break } if err != nil { log.Fatal(err) } i++ sheetData = append(sheetData, record) } name := strings.Replace(filename, ".csv", "", -1) data, err := g.SplicingData(sheetData, name) if err != nil { return fmt.Errorf("fileName:\"%v\" is err:%v", name, err) } if data == "" { return fmt.Errorf("ReadExcel|this.data is nil") } err = g.WriteNewFile(name, data) if err != nil { return err } return nil } //csv to mem func (g *Generate)CsvToMem(csvpath, filename string) error { csvfile, err := os.Open(csvpath+filename) if err != nil { return fmt.Errorf("ReadExcel|xlsx.OpenFile is err :%v", err) } defer csvfile.Close() r := csv.NewReader(csvfile) i := 0 sheetData := make([][]string, 0) name := strings.Replace(filename, ".csv", "", -1) for { record, err := r.Read() if err == io.EOF { break } if err != nil { log.Fatal(err) } if i < lineNumber { sheetData = append(sheetData, record) }else { //4行之后 fields := sheetData[1] typs := sheetData[2] //int, string keys := sheetData[3] //all, key, key1, key2 t := reflect.ValueOf(csvdata.CsvStruct[name]).Type() v := reflect.New(t).Elem() for idx, field := range fields { if field == "" || record[idx] == "" { continue } f := v.FieldByName(firstRuneToUpper(field)) switch typs[idx] { case "int": id, err := strconv.Atoi(record[idx]) if err != nil { return err } f.SetInt(int64(id)) case "string": f.SetString(record[idx]) } switch keys[idx] { case "all": case "key": case "key1": case "key2": default: } //fmt.Printf("%d, %s: %v, %v\n", idx, firstRuneToUpper(field), v.Field(idx), v.Interface()) } //csvdata.SetCsv(name, datam) } i++ } return nil } //拼装struct func (g *Generate) SplicingData(data [][]string, structName string) (string, error) { if len(data) != lineNumber { return "", fmt.Errorf("SplicingData|sheetName:%v col's len:%d is err", data, len(data)) } marks := data[0] fields := data[1] typs := data[2] keys := data[3] structData := fmt.Sprintf(structBegin, firstRuneToUpper(structName)) for i, field := range fields { err := g.CheckType(typs[i], structName) if err != nil { return "", err } switch keys[i] { case "key","all": structData += fmt.Sprintf(structValue, firstRuneToUpper(field), GetGoTByCustomerT(typs[i]), strings.ToLower(field), strings.ToLower(field)) if field != "" { structData += fmt.Sprintf(structRemarks, GbkToUtf8(marks[i])) } structData += fmt.Sprintf(structValueEnd) case "s": structData += fmt.Sprintf(structValueForServer, firstRuneToUpper(field), GetGoTByCustomerT(typs[i]), strings.ToLower(field)) if field != "" { structData += fmt.Sprintf(structRemarks,GbkToUtf8(marks[i])) } structData += fmt.Sprintf(structValueEnd) case "c": continue default: return "", fmt.Errorf("SplicingData|keys[%d]:\"%v\" is not in s,c,all", i, keys[i]) } } structData += structEnd return structData, nil } // 拼装好的struct写入新的文件 func (g *Generate) WriteNewFile(filename, data string) error { str := strings.Split(g.savePath, "/") if len(str) == 0 { return fmt.Errorf("WriteNewFile|len(str) is 0") } header = fmt.Sprintf(header, str[len(str)-1]) data = header + data fw, err := os.OpenFile(g.savePath +"/" +filename+".go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return fmt.Errorf("WriteNewFile|OpenFile is err:%v", err) } defer fw.Close() _, err = fw.Write([]byte(data)) if err != nil { return fmt.Errorf("WriteNewFile|Write is err:%v", err) } return nil } // 拼装好的初始化写入init.go文件 func (g *Generate) WriteInitPkg(data string) error { str := strings.Split(g.savePath, "/") if len(str) == 0 { return fmt.Errorf("WriteNewFile|len(str) is 0") } fw, err := os.OpenFile(g.savePath +"/init.go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return fmt.Errorf("WriteNewFile|OpenFile is err:%v", err) } defer fw.Close() _, err = fw.Write([]byte(fmt.Sprintf(initpkg, data))) if err != nil { return fmt.Errorf("WriteNewFile|Write is err:%v", err) } return nil } // 检测解析出来的字段类型是否符合要求 func (g *Generate) CheckType(dataType, structName string) error { res := strings.Index(g.allType, dataType) if res == -1 { return fmt.Errorf("CheckType|struct:\"%v\" dataType:\"%v\" is not in provide dataType", structName, dataType) } return nil } // 字符串首字母转换成大写 func firstRuneToUpper(str string) string { data := []byte(str) for k, v := range data { if k == 0 { first := []byte(strings.ToUpper(string(v))) newData := data[1:] data = append(first, newData...) break } } return string(data[:]) } // 判断是否存在汉字或者是否为默认的工作表 func hasChineseOrDefault(r string) bool { if strings.Index(r, "Sheet") != -1 { return true } for _, v := range []rune(r) { if unicode.Is(unicode.Han, v) { return true } } return false } // 读取Csv 转化为 struct func (g *Generate) ReadCsvToStruct(readPath string) error { files, err := ioutil.ReadDir(readPath) if err != nil { return fmt.Errorf("ReadExcel|ReadDir is err:%v", err) } data := "" for _, file := range files { if path.Ext(file.Name()) != ".csv" || hasChineseOrDefault(file.Name()) { continue } err := g.CsvToStruct(readPath, file.Name()) if err != nil { return err } name := strings.Replace(file.Name(), ".csv", "", -1) data += fmt.Sprintf(initline, name, firstRuneToUpper(name)) } g.WriteInitPkg(data) return nil } // 读取Csv 转化为 memory数据 func (g *Generate) ReadCsvToMemory(readPath string) error { files, err := ioutil.ReadDir(readPath) if err != nil { return fmt.Errorf("ReadExcel|ReadDir is err:%v", err) } for _, file := range files { if path.Ext(file.Name()) != ".csv" || hasChineseOrDefault(file.Name()) { continue } err := g.CsvToMem(readPath, file.Name()) if err != nil { return err } } return nil }