1. INI库
1.1. ini文件格式介绍
有默认(空)分区和命名的分区,没有给分区命名就是默认分区
,默认分区
必须写在任何一个命名分区
的上边。每个配置项通过换行来区分。
默认分区
:类似全局参数
命名分区
:类似局部参数
1.2. 功能
- 支持覆盖加载多个数据源(
file
,[]byte
,io.Reader
andio.ReadCloser
) - 支持递归读取键值
- 支持读取父子分区
- 支持读取自增键名
- 支持读取多行的键值
- 支持大量辅助方法
- 支持在读取时直接转换为 Go 语言类型
- 支持读取和 写入 分区和键的注释
- 轻松操作分区、键值和注释
- 在保存文件时分区和键值会保持原有的顺序
1.3. 安装
注:最低要求安装 Go 语言版本为 1.6
go get -v gopkg.in/ini.v1
1.4. 快速开始
- config.ini
app_name = blog
# possible values: DEBUG, INFO, WARNING, ERROR, FATAL
log_level = DEBUG
domain = google.com
[mysql]
domain = baidu.com
ip = %(domain)s
port = 3306
user = hallen
password = 123456
database = blog
[redis]
ip = %(domain)s
port = 6379
- main.go
package main
import (
"fmt"
"gopkg.in/ini.v1"
)
func main() {
cfg, err := ini.Load("./config.ini")
if err != nil {
fmt.Printf("读取配置文件出错... :%v\n", err)
return
}
//##############读取ini文件的方法###################
// 典型读取操作,默认分区可以使用空字符串表示
fmt.Println("APP NAME :", cfg.Section("").Key("app_name").String())
fmt.Println("MYSQL IP:", cfg.Section("mysql").Key("ip").String())
fmt.Println("MYSQL PORT:", cfg.Section("mysql").Key("port").String())
fmt.Println("REDIS IP:", cfg.Section("redis").Key("ip").String())
/*输出结果
APP NAME : blog
MYSQL IP: baidu.com
MYSQL PORT: 3306
REDIS IP: google.com
*/
//##############修改ini文件的方法###################
// 修改后,修改某个值然后进行保存。
// 修改默认分区节点的数据
cfg.Section("").Key("log_level").SetValue("DEV")
// 修改有节点的数据的方式
cfg.Section("redis").Key("port").SetValue("6378")
// 保存修改
cfg.SaveTo("./config.ini")
}
2. 数据源加载
2.1. Load
cfg, err := ini.Load("./config.ini")
if err != nil {
fmt.Printf("读取配置文件出错... :%v\n", err)
return
}
2.2. ShadowLoad
ShadowLoad()
具有与Load()
函数完全相同的功能,只是它允许读取同名Key(同个键名包含多个值
)。
cfg, err := ini.ShadowLoad("./config.ini")
if err != nil {
fmt.Printf("读取配置文件出错... :%v\n", err)
return
}
/* config.ini
[remote]
url = https://github.com/test1.git
url = https://github.com/test2.git
*/
cfg.Section("remote").Key("url").ValueWithShadows()
// Result: []string{
// "https://github.com/test1.git",
// "https://github.com/test2.git",
// }
3. 读取数据
3.1. 操作分区(Section)
3.1.1. 获取所有分区
//获取所有分区对象
secs := cfg.Sections()
//获取所有分区名称
names := cfg.SectionStrings()
3.1.2. 指定分区
GetSection()
和Section()
功能一样。只是Section()
如果没找到分区,会自动创建一个分区保存在ini文件,GetSection()
不会
3.1.2.1. GetSection
sec, err := cfg.GetSection("mysql")
3.1.2.2. Section
//获取指定分区
sec := cfg.Section("mysql")
//获取默认分区,使用空字符串代替分区名
sec := cfg.Section("")
3.2. 操作键(Key)
3.2.1. 获取所有键名
//获取所有键名
names := cfg.Section("mysql").KeyStrings()
3.2.2. 指定键
GetKey()
和Key()
功能一样。只是Key()
如果没找到分区,会自动创建一个Key保存在ini文件,GetKey()
不会
3.2.2.1. GetKey
key, err := cfg.Section("mysql").GetKey("ip")
3.2.2.2. Key
key := cfg.Section("mysql").Key("ip")
3.3. 操作键值(Value)
3.3.1. 获取所有键值
//获取所有键值
keys := cfg.Section("mysql").Keys()
3.3.2. 获取指定键值
3.3.2.1. 获取原值
val := cfg.Section("mysql").Key("ip").Value()
3.3.2.2. 获取类型转换值
// 布尔值的规则:
// true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
// false 当值为:0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
val := cfg.Section("").Key("key name").String()
v, err = cfg.Section("").Key("BOOL").Bool()
v, err = cfg.Section("").Key("FLOAT64").Float64()
v, err = cfg.Section("").Key("INT").Int()
v, err = cfg.Section("").Key("INT64").Int64()
v, err = cfg.Section("").Key("UINT").Uint()
v, err = cfg.Section("").Key("UINT64").Uint64()
v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
v, err = cfg.Section("").Key("TIME").Time() // RFC3339
// 由 Must 开头的方法名允许接收一个相同类型的参数来作为默认值,
// 当键不存在或者转换失败时,则会直接返回该默认值。
// 但是,MustString 方法必须传递一个默认值。
v = cfg.Section("").Key("String").MustString("default")
v = cfg.Section("").Key("BOOL").MustBool()
v = cfg.Section("").Key("FLOAT64").MustFloat64()
v = cfg.Section("").Key("INT").MustInt()
v = cfg.Section("").Key("INT64").MustInt64()
v = cfg.Section("").Key("UINT").MustUint()
v = cfg.Section("").Key("UINT64").MustUint64()
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
v = cfg.Section("").Key("TIME").MustTime() // RFC3339
3.3.3. 占位符
在配置文件中,可以使用占位符%(domain)s
表示用之前已定义的键domain
的值来替换,这里的s
表示值为字符串类型。如果某个键在子分区中不存在,则会在它的父分区中再次查找,直到没有父分区为止,就近原则。
在快速开始中,可以看到例子。
4. 写入数据
4.1. 修改键值(Value)
//修改数据
cfg.Section("redis").Key("port").SetValue("6378")
//保存
cfg.SaveTo("./config.ini")
5. 结构体与分区双向映射
5.1. Struct Tag
type Embeded struct {
Dates []time.Time `delim:"|" comment:"Time data"` //delim:设置分隔符,默认为逗号;comment:注释
Places []string `ini:"places,omitempty"` //omitempty:如果数据为空不映射值
None []int `ini:",omitempty"` //`ini:字段名,是否忽略空值,allowshadow`
}
type Author struct {
Name string `ini:"NAME"`
Male bool
Age int `comment:"Author's age"`
GPA float64
NeverMind string `ini:"-"` //-:忽略该字段
*Embeded `comment:"Embeded section"`
}
5.2. 文件映射到结构
5.2.1. MapTo
package main
import (
"fmt"
"gopkg.in/ini.v1"
)
type Conf struct {
AppName string `ini:"app_name"`
LogLevel string `ini:"log_level"`
Domain string `ini:"domain"`
Mysql `ini:"mysql"`
Redis `ini:"redis"`
}
type Mysql struct {
Domain string `ini:"domain"`
IP string `ini:"ip"`
Port string `ini:"port"`
User string `ini:"user"`
Password string `ini:"password"`
Database string `ini:"-"`
}
type Redis struct {
IP string `ini:"ip"`
Port string `ini:"port"`
User string `ini:"user"`
}
func main() {
cfg, err := ini.Load("./config.ini")
if err != nil {
fmt.Printf("读取配置文件出错... :%v\n", err)
return
}
config := &Conf{
//设置默认值,如果键未找到或者类型错误,才会赋值。否则使用原值
Redis: Redis{
Port: "2233",
User: "redis_user",
},
}
err = cfg.MapTo(config)
if err != nil {
fmt.Println("cfg.MapTo err :", err)
}
fmt.Println(config)
//&{blog DEBUG google.com {baidu.com baidu.com 3306 hallen 123456 } {google.com 6379 redis_user}}
}
5.3. 结构体反射到文件
5.3.1. ReflectFrom
通过Struct Tag反射,保存生成ini文件
package main
import (
"time"
"gopkg.in/ini.v1"
)
type Embeded struct {
Dates []time.Time `delim:"|" comment:"Time data"`
Places []string `ini:"places,omitempty"`
None []int `ini:",omitempty"`
}
type Author struct {
Name string `ini:"NAME"`
Male bool
Age int `comment:"Author's age"`
GPA float64
NeverMind string `ini:"-"`
*Embeded `comment:"Embeded section"`
}
func main() {
a := &Author{"Unknwon", true, 21, 2.8, "",
&Embeded{
[]time.Time{time.Now(), time.Now()},
[]string{"HangZhou", "Boston"},
[]int{},
}}
cfg := ini.Empty()
err := ini.ReflectFrom(cfg, a)
if err != nil {
return
}
//保存文件
cfg.SaveTo("./test.ini")
}
- test.ini
NAME = Unknwon
Male = true
; Author's age
Age = 21
GPA = 2.8
; Embeded section
[Embeded]
; Time data
Dates = 2022-11-17T14:38:02+08:00|2022-11-17T14:38:02+08:00
places = HangZhou,Boston