INI 是 Initialization File 的缩写,也就是初始化文件,原来是 Windows 系统配置文件所采用的存储格式,不过因为其使用简单,所以后来被广泛应用。
这里介绍如何通过 GoLang 解析。
简介
INI 配置文件由节 (Section)、健 (Key)、值 (Value) 组成,其中注释一般是以 ;
#
开头,尽量放到每行的开始,有些解析器可能会报错,示例如下。
; Here is comments.
[Section]
Key=Value
这里介绍 github.com/go-ini/ini 的使用方式,可以参考官网 ini.unknwon.io 中的介绍。通常使用 gopkg.in/ini.v1
,也就是指定一个版本,如果要使用最新的版本,可以引用 github.com/go-ini/ini
。
示例
如下是一个配置文件。
# possible values : production, development
app_mode = development
[paths]
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
data = /home/git/grafana
[server]
# Protocol (http or https)
protocol = http
# The http port to use
http_port = 9999
# Redirect to correct domain if host header does not match domain
# Prevents DNS rebinding attacks
enforce_domain = true
然后可以通过如下代码解析。
package main
import (
"fmt"
"os"
"gopkg.in/ini.v1"
)
func main() {
cfg, err := ini.Load("my.ini")
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(1)
}
// Classic read of values, default section can be represented as empty string
fmt.Println("App Mode:", cfg.Section("").Key("app_mode").String())
fmt.Println("Data Path:", cfg.Section("paths").Key("data").String())
// Let's do some candidate value limitation
fmt.Println("Server Protocol:",
cfg.Section("server").Key("protocol").In("http", []string{"http", "https"}))
// Value read that is not in candidates will be discarded and fall back to given default value
fmt.Println("Email Protocol:",
cfg.Section("server").Key("protocol").In("smtp", []string{"imap", "smtp"}))
// Try out auto-type conversion
fmt.Printf("Port Number: (%[1]T) %[1]d\n", cfg.Section("server").Key("http_port").MustInt(9999))
fmt.Printf("Enforce Domain: (%[1]T) %[1]v\n", cfg.Section("server").Key("enforce_domain").MustBool(false))
// Now, make some changes and save it
cfg.Section("").Key("app_mode").SetValue("production")
cfg.SaveTo("my.ini.local")
}
基础操作
操作分区 Section
//----- 创建一个分区
err := cfg.NewSection("SectionName")
//----- 获取分区,默认分区使用空字符串 "" 代替
section, err := cfg.GetSection("SectionName")
//----- 如果能确定分区必然存在,可使用如下方法
section := cfg.Section("SectionName")
//----- 获取所有分区对象或名称
sections := cfg.Sections()
names := cfg.SectionStrings()
如果通过 cfg.Section("SectionName")
获取的分区是不存在的,那么会自动创建并返回一个对应的分区对象。
操作键 Key
//----- 获取某个分区下的键
key, err := cfg.Section("SectionName").GetKey("KeyName")
//----- 也可以直接获取键而忽略错误
key := cfg.Section("SectionName").Key("KeyName")
//----- 判断某个键是否存在
yes := cfg.Section("SectionName").HasKey("KeyName")
//----- 创建一个新的键
err := cfg.Section("SectionName").NewKey("Name", "Value")
//----- 获取分区下的所有键或键名
keys := cfg.Section("SectionName").Keys()
names := cfg.Section("SectionName").KeyStrings()
操作键值 Value
//----- 获取一个类型为字符串的值
val := cfg.Section("SectionName").Key("KeyName").String()
//----- 获取值的同时通过自定义函数进行处理验证
val := cfg.Section("SectionName").Key("KeyName").Validate(func(in string) string {
if len(in) == 0 {
return "default"
}
return in
})
//----- 也可以直接获取原值
val := cfg.Section("SectionName").Key("KeyName").Value()
//----- 判断某个原值是否存在
yes := cfg.Section("SectionName").HasValue("Value")
//----- 获取值,会进行转换
val, err = cfg.Section("SectionName").Key("KeyName").Float64()
val = cfg.Section("SectionName").Key("KeyName").MustFloat64()
val = cfg.Section("SectionName").Key("KeyName").MustFloat64(3.14)
对于获取值的方法 Must
开头允许接收一个相同类型的参数作为默认值,还可以使用 Bool
Int
Int64
Uint
Uint64
TimeFormat
Time
,时间可用参数指定时间格式,例如 TimeFormat(time.RFC3339)
,以及使用当前时间作为默认值 MustTimeFormat(time.RFC3339, time.Now())
。
另外,对于布尔值来说,其中 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
。