package serverproto import ( "encoding/csv" "errors" "fmt" "os" "reflect" "strconv" "strings" ) ////https://github.com/gocarina/gocsv //配置表集合 func loadCsvCfg(fileName string, cfg interface{}) { file, err := os.OpenFile(fileName, os.O_RDONLY, os.ModePerm) if err != nil { panic(err) } defer file.Close() csvReader := csv.NewReader(file) if csvReader == nil { panic(errors.New(fmt.Sprintf("%v:csv reader nil file", fileName))) } err = csvUnmarshal(csvReader, cfg) if err != nil { panic(errors.New(fmt.Sprintf("%v:%v", fileName, err))) } csvReader = nil } func csvUnmarshal(r *csv.Reader, cfg interface{}) error { cfgValue := reflect.ValueOf(cfg) if cfgValue.Kind() == reflect.Ptr { cfgValue = cfgValue.Elem() } cfgValueType := cfgValue.Type() //配置文件类型判断 if cfgValueType.Kind() != reflect.Slice && cfgValueType.Kind() != reflect.Chan && cfgValueType.Kind() != reflect.Array { //return fmt.Errorf("cfg type error:%v\n",cfg) } csvRows, err := r.ReadAll() if err != nil { return err } csvRowsNum := len(csvRows) if csvRowsNum == 0 { return errors.New("empty csv file!") } //判断容量 cfgPtr := &cfgValue switch cfgPtr.Kind() { case reflect.Array: if cfgPtr.Len() < csvRowsNum-1 { return fmt.Errorf("capacity problem") } case reflect.Slice: if !cfgPtr.CanAddr() && cfgPtr.Len() < csvRowsNum-3 { return fmt.Errorf("capacity problem") } else if cfgPtr.CanAddr() && cfgPtr.Len() < csvRowsNum-3 { cfgPtr.Set(reflect.MakeSlice(cfgPtr.Type(), csvRowsNum-3, csvRowsNum-3)) } } stInfo := getStructInfo(cfgValueType.Elem()) if len(stInfo.Fields) <= 0 { return errors.New("fields empty") } csvHeaders := csvRows[1] csvData := csvRows[3:] //数据下标对应 csvHeadersLabels := make(map[int]*fieldInfo, len(stInfo.Fields)) //[pos, fieldInfo] headerMap := map[string]int{} // [name,fieldPos] for i, csvHeader := range csvHeaders { currentHeaderPos := headerMap[csvHeader] if info := getFieldPos(csvHeader, stInfo, currentHeaderPos); info != nil { info.index = i csvHeadersLabels[i] = info } } for i, row := range csvData { var cfgTemp reflect.Value if cfgValueType.Elem().Kind() == reflect.Ptr { cfgTemp = reflect.New(cfgValueType.Elem().Elem()) } else { cfgTemp = reflect.New(cfgValueType.Elem()) } for j, colData := range row { if info, ok := csvHeadersLabels[j]; ok { elm := cfgTemp.Elem() setFieldData(&elm, colData, info) } } cfgValue.Index(i).Set(cfgTemp) } headerMap = nil csvHeadersLabels = nil csvHeaders = nil csvRows = nil return nil } type structInfo struct { Fields []fieldInfo } type fieldInfo struct { keys string empty bool index int } func getStructInfo(stType reflect.Type) *structInfo { if stType.Kind() == reflect.Ptr { stType = stType.Elem() } fieldList := getFieldInfo(stType, []int{}) return &structInfo{ Fields: fieldList, } } func getFieldInfo(stType reflect.Type, index []int) []fieldInfo { fieldNum := stType.NumField() fieldList := make([]fieldInfo, 0, fieldNum) for i := 0; i < fieldNum; i++ { field := stType.Field(i) if field.PkgPath != "" { continue } info := fieldInfo{} info.keys = field.Tag.Get("csv") fieldList = append(fieldList, info) } return fieldList } func getFieldPos(header string, stInfo *structInfo, currenPos int) *fieldInfo { for _, f := range stInfo.Fields { if f.keys == header { return &f } } return nil } func setFieldData(value *reflect.Value, data string, field *fieldInfo) { v := (*value).FieldByIndex([]int{field.index}) if v.Kind() == reflect.Ptr { if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } //获取指针指向的变量,后续才能做赋值操作 v = v.Elem() } inValue := reflect.ValueOf(data) switch v.Interface().(type) { case string: v.SetString(data) case bool: if strings.EqualFold(strings.ToLower(data), "true") { v.SetBool(true) } else if strings.EqualFold(strings.ToLower(data), "false") { v.SetBool(false) } case int, int32, uint32: s := strings.TrimSpace(inValue.String()) if s == "" { v.SetInt(0) } i, err := strconv.ParseInt(s, 0, 64) if err == nil { v.SetInt(i) } case float32: s := strings.TrimSpace(inValue.String()) if s == "" { v.SetFloat(0) } i, err := strconv.ParseFloat(s, 64) if err == nil { v.SetFloat(i) } case []string: dataList := strings.Split(data, ";") v.Set(reflect.ValueOf(dataList)) default: panic(errors.New("field type error")) } }