练习题1

加载ini配置文件
mian.go

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "reflect"
  6. "strconv"
  7. "strings"
  8. )
  9. // MySQL 结构体
  10. type MySQL struct {
  11. Addr string `ini:"addr"`
  12. Port int64 `ini:"port"`
  13. Name string `ini:"name"`
  14. Pwd string `ini:"pwd"`
  15. }
  16. // Redis 结构体
  17. type Redis struct {
  18. Username string `ini:"username"`
  19. Password string `ini:"password"`
  20. }
  21. // Config 配置文件结构体
  22. type Config struct {
  23. MySQL `ini:"mysql"`
  24. Redis `ini:"redis"`
  25. }
  26. func loadConfig(fileName string, data interface{}) (err error) {
  27. /*
  28. 1、判断传入的data是否是指针类型,只有指针类型才进行下面步骤
  29. 2、判断传入的指针类型参数是否为结构体指针
  30. 3、读取文件得到字节类型数据
  31. 4、逐行读取数据
  32. 4.1、如果有注释,则跳过处理
  33. 4.2、如果是[]包含的则表示节点
  34. 4.3、如果不是上述的则对其进行按'='切割
  35. 4.4、对切割的数据进行存储到结构体
  36. */
  37. t := reflect.TypeOf(data)
  38. if t.Kind() != reflect.Ptr {
  39. err = fmt.Errorf("data param must be a pointer")
  40. return
  41. }
  42. if t.Elem().Kind() != reflect.Struct {
  43. err = fmt.Errorf("data param must be a struct pointer")
  44. return
  45. }
  46. // 读取文本文件
  47. file, err := ioutil.ReadFile(fileName)
  48. if err != nil {
  49. return
  50. }
  51. // 对读取出来的文本文件进行切片
  52. fileSlice := strings.Split(string(file), "\r\n")
  53. // 记录反射获取的结构体名
  54. var structName string
  55. // var valueType
  56. // 遍历切片
  57. for idx, line := range fileSlice {
  58. // 对line进行格式化
  59. line = strings.TrimSpace(line)
  60. // 如果是空行则跳过
  61. if len(line) == 0 {
  62. continue
  63. }
  64. // 如果为注释则跳过
  65. if strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") {
  66. continue
  67. }
  68. // 如果以[开头再进行进一步处理
  69. if strings.HasPrefix(line, "[") {
  70. // 如果结尾没有 ] ,则抛出错误
  71. if !strings.HasSuffix(line, "]") {
  72. err = fmt.Errorf("synx error in line %d", idx+1)
  73. return
  74. }
  75. // 如果以[开头,以]结尾,但是没有内容,则抛出错误
  76. selectName := strings.TrimSpace(line[1 : len(line)-1])
  77. if len(selectName) == 0 {
  78. err = fmt.Errorf("synx error in line %d,the content con't be empty", idx+1)
  79. return
  80. }
  81. // 如果上述条件都满足,则根据节点名称找到对应结构体
  82. // 读取data的value
  83. for i := 0; i < t.Elem().NumField(); i++ {
  84. field := t.Elem().Field(i)
  85. // 获取tag,反射获取其内容
  86. // fmt.Println(field.Tag.Get("ini"))
  87. // fmt.Println(selectName)
  88. if field.Tag.Get("ini") == selectName {
  89. // 说明找到了对应的结构体,然后讲结构体记录下来
  90. structName = field.Name
  91. fmt.Printf("find %s struct from %s in data\n", structName, selectName)
  92. break
  93. } else {
  94. structName = ""
  95. }
  96. }
  97. } else {
  98. // 上面通过反射找到structName,判断其是不是struct类型,如果不是抛出错误
  99. if structName != "" {
  100. v := reflect.ValueOf(data)
  101. sValue := v.Elem().FieldByName(structName)
  102. sType := sValue.Type()
  103. if sType.Kind() != reflect.Struct {
  104. err = fmt.Errorf("%s in data must be a struct type", structName)
  105. return
  106. }
  107. // 判断文本当前行是否有等号,并且等号左边是key,右边是value
  108. if strings.Contains(line, "=") {
  109. // 获取=所在的索引
  110. index := strings.Index(line, "=")
  111. key := strings.TrimSpace(line[:index])
  112. value := strings.TrimSpace(line[index+1:])
  113. if len(key) == 0 {
  114. err = fmt.Errorf("synx error. line: %d", idx)
  115. return
  116. }
  117. // fmt.Printf("key: %s value: %s\n", key, value)
  118. // 通过反射找到key对应的结构体中的变量名
  119. var fieldName string
  120. var filedType reflect.StructField
  121. for i := 0; i < sValue.NumField(); i++ {
  122. // fmt.Println(sValue.Field(i))
  123. field := sType.Field(i)
  124. filedType = field
  125. if field.Tag.Get("ini") == key {
  126. fieldName = field.Name
  127. break
  128. }
  129. }
  130. fileObj := sValue.FieldByName(fieldName)
  131. // fmt.Print(fieldName, filedType.Type.Kind())
  132. // 根据不同的类型进行写入操作
  133. switch filedType.Type.Kind() {
  134. case reflect.String:
  135. fileObj.SetString(value)
  136. case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8:
  137. // 对value进行转换,由string-->int
  138. var valueInt int64
  139. valueInt, err = strconv.ParseInt(value, 10, 64)
  140. if err != nil {
  141. err = fmt.Errorf("line: %d synx error", idx)
  142. return
  143. }
  144. fileObj.SetInt(valueInt)
  145. case reflect.Float32, reflect.Float64:
  146. // 对value进行转换,由string-->float
  147. var valueFloat float64
  148. valueFloat, err = strconv.ParseFloat(value, 64)
  149. if err != nil {
  150. err = fmt.Errorf("line: %d synx error", idx)
  151. return
  152. }
  153. fileObj.SetFloat(valueFloat)
  154. case reflect.Bool:
  155. var valueBool bool
  156. valueBool, err = strconv.ParseBool(value)
  157. if err != nil {
  158. err = fmt.Errorf("line: %d synx error", idx)
  159. return
  160. }
  161. fileObj.SetBool(valueBool)
  162. default:
  163. err = fmt.Errorf("line: %d value type error", idx)
  164. return
  165. }
  166. } else {
  167. err = fmt.Errorf("synx error. line: %d", idx)
  168. return
  169. }
  170. }
  171. }
  172. }
  173. return
  174. }
  175. func main() {
  176. // 声明结构体
  177. var cfg Config
  178. // fmt.Printf("%T\n", &cfg)
  179. err := loadConfig("./my.ini", &cfg)
  180. if err != nil {
  181. fmt.Println("conf load failed,err:", err)
  182. }
  183. fmt.Println(cfg)
  184. }

my.ini

  1. [mysql]
  2. addr = localhost
  3. port = 3306
  4. name = root
  5. pwd = root
  6. ; [
  7. ; []
  8. [redis]
  9. username=root
  10. password=910416