INI 文件由多行文本组成,整个配置由[ ]
拆分为多个“段”(section)。每个段中又以=
分割为“键”和“值”。
INI 文件以;
置于行首视为注释,注释后将不会被处理和识别
[sectionl]
key1=value1
;key2=value2
[section2]
package config
import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"os"
"reflect"
"strings"
)
type Ini struct {
Path string
}
//根据段名,读取配置到结构体
func (i *Ini) Read(duan string, conf any) error {
if i.Path == "" {
return errors.New("path not defined")
}
val := reflect.ValueOf(conf)
if val.Kind() != reflect.Ptr {
return errors.New("conf must be a Struct")
}
val = val.Elem()
typ := reflect.TypeOf(conf).Elem()
if val.Kind() != reflect.Struct {
return errors.New("conf must be a Struct")
}
m, err := ParseFile(i.Path)
if err != nil {
return err
}
data := m[duan]
if len(data) == 0 {
return nil
}
for num := 0; num < typ.NumField(); num++ {
field := typ.Field(num)
tag := field.Tag.Get("ini")
val.Field(num).SetString(data[tag])
}
return nil
}
//将结构体以指定的段名写入ini
func (i *Ini) Write(duan string, conf any) error {
if i.Path == "" {
return errors.New("path not defined")
}
typ := reflect.TypeOf(conf)
val := reflect.ValueOf(conf)
//如果以指针传入
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
if typ.Kind() != reflect.Struct {
return errors.New("conf must a struct")
}
val = val.Elem()
} else if typ.Kind() != reflect.Struct {
return errors.New("conf must a struct")
}
m := make(map[string]string)
for i := 0; i < typ.NumField(); i++ {
tag := typ.Field(i).Tag.Get("ini")
value := val.Field(i).String()
m[tag] = value
}
return WriteFile(i.Path, duan, m)
}
// ParseFile 将ini文件中的数据以map切片形式读出
func ParseFile(path string) (map[string]map[string]string, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
reader := bufio.NewReader(f)
m := make(map[string]map[string]string)
//表示当前记录的段名
duanName := ""
for {
s, err := reader.ReadString('\n')
if err != nil {
break
}
// 去掉行两边的空格
s = strings.TrimSpace(s)
//如果是空行或者注释行跳过
if s == "" || s[0] == ';' {
continue
}
//如果以[开头并以]结尾,说明是段
if s[0] == '[' && s[len(s)-1] == ']' {
duanName = s[1 : len(s)-1]
m[duanName] = make(map[string]string)
continue
}
kv := strings.Split(s, "=")
if duanName != "" {
key := strings.TrimSpace(kv[0])
val := strings.TrimSpace(kv[1])
m[duanName][key] = val
}
}
return m, nil
}
// WriteFile 将map作为指定段的内容写入ini,其余的配置不变
func WriteFile(path string, duan string, m map[string]string) error {
b, err := ioutil.ReadFile(path)
if err != nil {
return err
}
data := string(b)
//将数据按照段切分为切片
dataSlice := strings.Split(data, "[")
dataSlice = dataSlice[1:]
fmt.Println(dataSlice)
duan = fmt.Sprintf("[%s]", duan)
index := -1
//判断该段是否已经存在
for i := 0; i < len(dataSlice); i++ {
dataSlice[i] = "[" + dataSlice[i]
fmt.Println(dataSlice[i])
if strings.Index(dataSlice[i], duan) != -1 {
index = i
}
}
duanData := "\n" + duan + "\n"
//将参数封装为字符串
for k, v := range m {
duanData += fmt.Sprintf("%s = %s\n", k, v)
}
//判断该段是否已经存在,存在就覆写,不存在就追加一个
if index != -1 {
dataSlice[index] = duanData
} else {
dataSlice = append(dataSlice, duanData)
}
data = strings.Join(dataSlice, "")
err = ioutil.WriteFile(path, []byte(data), 0777)
if err != nil {
return err
}
return nil
}
package config
type Mysql struct {
Driver string `ini:"driver"`
UserName string `ini:"username"`
PassWord string `ini:"password"`
}
type Oracle struct {
UserName string `ini:"username"`
PassWord string `ini:"password"`
}
[mysql]
username = root
password = 123456
driver = com.jdbc.mysql
[oracle]
username = orl
password = 123456
读
package main
import (
"demo/config"
"fmt"
)
func main() {
i := &config.Ini{"./db.ini"}
mysql := config.Mysql{}
oracle := config.Oracle{}
err := i.Read("mysql", &mysql)
if err != nil {
fmt.Println(err)
}
err = i.Read("oracle", &oracle)
if err != nil {
fmt.Println(err)
}
fmt.Println(mysql) //{com.jdbc.mysql root 123456}
fmt.Println(oracle) //{orl 123456}
}
写
package main
import (
"demo/config"
"fmt"
)
func main() {
m := config.Mysql{"con.test.mysql", "mysql", "78787878"}
ini := config.Ini{"./db.ini"}
err := ini.Write("mysql", m)
if err != nil {
fmt.Println(err)
}
}