docs: ini 文件解析

reflect 反射 解析 ini
This commit is contained in:
Shikong 2021-10-08 00:34:57 +08:00
parent ceea6dd095
commit ce1b89f984
2 changed files with 188 additions and 0 deletions

12
base/reflect/ini/conf.ini Normal file
View File

@ -0,0 +1,12 @@
; mysql 数据库配置
[mysql]
host=127.0.0.1
port=3306
user=root
password=12341234
# redis 配置
[redis]
host= 127.0.0.1
port = 6379
password =12341234
db=0

176
base/reflect/ini/main.go Normal file
View File

@ -0,0 +1,176 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"reflect"
"regexp"
"strconv"
"strings"
)
type IniConfig struct {
Mysql MysqlConfig `ini:"mysql"`
Redis RedisConfig `ini:"redis"`
}
type MysqlConfig struct {
Host string `ini:"host"`
Port int `ini:"port"`
User string `ini:"user"`
Password string `ini:"password"`
}
type RedisConfig struct {
Host string `ini:"host"`
Port int `ini:"port"`
Password string `ini:"password"`
Db int `ini:"db"`
}
func loadIni(filePath string, v interface{}) (err error) {
// 传入的 接收值 v 必须为指针类型 因为需要对齐进行赋值操作
t := reflect.TypeOf(v)
if v == nil || t.Kind() != reflect.Ptr {
return fmt.Errorf("接收者必须为指针类型\n")
}
el := t.Elem()
if el.Kind() != reflect.Struct {
return fmt.Errorf("接收者必须为一个结构体对象\n")
}
// 打开文件
f, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
if err != nil {
return err
}
// 读取整个文件
b, _ := ioutil.ReadAll(f)
// 释放文件占用
_ = f.Close()
// ini 文件中
// ; 开头的为注释
// [xxx] 为节
// key=value 为键值对
// 读取文件
conf := string(b)
// 替换换行符 \r\n 为 \n
conf = strings.Replace(conf, "\r\n", "\n", -1)
// 根据换行符 \n 分割行
confSplit := strings.Split(conf, "\n")
//fmt.Printf("%#v\n", confSplit)
var curSection string
for _, line := range confSplit {
// 去除 首尾 空格符
trimReg, _ := regexp.Compile("\\s")
line = trimReg.ReplaceAllLiteralString(line, "")
// 如果是以 ; 或 # 开头 的 视为注释 直接跳过
annotationReg, _ := regexp.Compile("^[;|#]")
if annotationReg.MatchString(line) {
continue
}
sectionReg, _ := regexp.Compile("^\\[(.*)]$")
if sectionReg.MatchString(line) {
sectionMatch := sectionReg.FindAllStringSubmatch(line, 1)
if len(sectionMatch) < 1 {
return fmt.Errorf("无效的 节 %s\n", line)
}
if len(sectionMatch[0]) < 2 {
return fmt.Errorf("无效的 节 %s\n", line)
}
section := sectionMatch[0][1]
//fmt.Printf("节: %s\n", line)
value := reflect.ValueOf(v).Elem()
for i := 0; i < el.NumField(); i++ {
field := el.Field(i)
if field.Tag.Get("ini") == section {
item := value.FieldByName(field.Name)
if item.Kind() == reflect.Ptr {
return fmt.Errorf("接收者 %s 不能为指针\n", section)
}
//if item.Kind() == reflect.Struct {
// fmt.Printf("%s 为结构体\n", section)
//}
curSection = field.Name
break
}
}
continue
}
if strings.EqualFold(curSection, "") {
continue
}
keyValueReg, _ := regexp.Compile("^(.*)=(.*)$")
if keyValueReg.MatchString(line) {
keyValueMatch := keyValueReg.FindAllStringSubmatch(line, -1)
if len(keyValueMatch) < 1 {
//fmt.Println("未找到 匹配的 键值对")
continue
}
keyValueSubMatch := keyValueMatch[0]
if len(keyValueSubMatch) < 3 {
//fmt.Println("未找到 匹配的 键值对")
continue
}
k := keyValueSubMatch[1]
val := keyValueSubMatch[2]
//fmt.Printf("键值对: k: %#v => v: %#v\n", k, val)
sectionReflectType, _ := reflect.TypeOf(v).Elem().FieldByName(curSection)
sectionType := sectionReflectType.Type
sectionVal := reflect.ValueOf(v).Elem().FieldByName(curSection)
if sectionVal.Kind() != reflect.Struct {
return fmt.Errorf("section 接收者 需为 struct 类型\n")
}
for i := 0; i < sectionVal.NumField(); i++ {
selectionKey := sectionType.Field(i).Tag.Get("ini")
if strings.EqualFold(selectionKey, k) {
//fmt.Println(selectionKey, sectionVal.Field(i).Kind())
switch sectionVal.Field(i).Kind() {
case reflect.Int:
intVal, err := strconv.Atoi(val)
if err != nil {
return err
}
sectionVal.Field(i).SetInt(int64(intVal))
case reflect.String:
sectionVal.Field(i).SetString(val)
}
break
}
}
continue
}
fmt.Println(line)
}
return
}
func main() {
config := new(IniConfig)
err := loadIni("./base/reflect/ini/conf.ini", config)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v\n", config)
}