GoLang INI 配置文件解析

2020-08-09 language golang

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