go-ini

Kesa...大约 5 分钟golanggolang daily lib

简介

ini是windows上常用的配置文件格式。MySQL的Windows版本就是使用ini格式存储配置的。go-ini是Golang中用于操作ini文件的三方库

ini文件简介

.ini 文件是Initialization File的缩写,即初始化文件 ,是windows的系统配置文件所采用的存储格式

文件格式

ini文件由节、键、值组成,注释使用;

; commnet line
[section]
key1=value1
key2=value2

快速开始 quick start

go get -u gopkg.in/ini.v1 v1.63.2
; config.ini
app_name = awesome web

; possible values: DEBUG, INFO, WARNING, ERROR, FATAL
log_level = DEBUG

[mysql]
ip = 127.0.0.1
port = 3306
user = u
password = 123456
database = awesome

[redis]
ip = 127.0.0.1
port = 6381

package main

import (
	"fmt"
	"gopkg.in/ini.v1"
	"log"
)

func main() {
	config, err := ini.Load("../resources/config.ini")
	if err != nil {
		log.Fatal("read config error ", err)
	}
	fmt.Println("App name:", config.Section("").Key("app_name").String())
	fmt.Println("Log level:", config.Section("").Key("log_level").String())
	fmt.Println("Mysql ip:", config.Section("mysql").Key("ip").String())
	mysqlPort, err := config.Section("mysql").Key("port").Int()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(mysqlPort)
	fmt.Println("Redis ip:", config.Section("redis").Key("ip").String())
	redisPort, err := config.Section("redis").Key("port").Int()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(redisPort)
}

$ go run ./main.go 
App name: awesome web
Log level: DEBUG
Mysql ip: 127.0.0.1
3306
Redis ip: 127.0.0.1
6381

在ini文件中,每个键值对占用一行,中间使用=分隔。;开头为注释,文件内容以section组织,section[section_name]开始,下一个section结束。所有的section前的内容属于默认section

使用go-ini读取配置文件步骤如下:

  • 首先调用init.load加载文件,获取配置对象config(类型ini/File)
  • 之后调用Section方法获取section,默认section名为"",也可使用ini.DefaultSection
  • 调用Key方法获取key(类型ini/Key)对象
  • 根据value的类型调用不同的方法获取值,如String,Int
config.Section("section_name").Key("key_name").[Value(),String(),Int()...]

配置文件中存储的均为字符串,故String()仅返回值;若类型为int/uint/float64时,转换可能失败,故Int()/Uint()/Float64()返回值和error

MustType 便捷方法

若每次取值需要进行错误判断,那么Code将会变得繁琐。为此,go-ini也提供对应的MustType方法,这种方法仅返回一个值。同时可接收变长参数,若无法进行类型转换,取参数列表中的第一个返回

; config_2.ini
[redis]
port=port_6379
package main

import (
	"fmt"
	"gopkg.in/ini.v1"
)

func main() {
	config, err := ini.Load("../resources/config_2.ini")
	if err != nil {
		fmt.Println("read config error :", err)
	}
	fmt.Println("Before call MustType")
	port, err := config.Section("redis").Key("port").Int()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("port:", port)
	fmt.Println("Call MustType")
	port = config.Section("redis").Key("port").MustInt(6379)
	fmt.Println("port:", port)
	port, err = config.Section("redis").Key("port").Int()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("After call MustType")
	fmt.Println("port:", port)
}

$ go run ./main.go 
Before call MustType
strconv.ParseInt: parsing "port_6379": invalid syntax
port: 0
Call MustType
port: 6379
After call MustType
port: 6379

第一次调用Int方法返回错误,在调用MustInt将参数设置为6379之后,再次调用Int返回6379

// gopkg.in/ini.v1/key.go
func (k *Key) MustInt(defaultVal ...int) int {
  val, err := k.Int()
  if len(defaultVal) > 0 && err != nil {
    k.value = strconv.FormatInt(int64(defaultVal[0]), 10)
    return defaultVal[0]
  }
  return val
}

Section 操作

获取信息

在加载配置之后,可以通过Sections获取所有sectionm,SectionStrings方法获取所有section name.

package main

import (
	"fmt"
	"gopkg.in/ini.v1"
	"log"
)

func main() {
	config, err := ini.Load("../resources/config.ini")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Sections:%#v\n\n", config.Sections())
	fmt.Printf("Section names:%v\n\n", config.SectionStrings())

	newSection := config.Section("new_section")
	fmt.Printf("New section:%#v\n\n", newSection)
	fmt.Printf("Section names:%v\n\n", config.SectionStrings())
}

$ go run ./main.go
Sections:[]*ini.Section{(*ini.Section)(0xc00011a620), (*ini.Section)(0xc00011a770), (*ini.Section)(0xc00011aa10)}

Section names:[DEFAULT mysql redis]

New section:&ini.Section{f:(*ini.File)(0xc00012c000), Comment:"", name:"new_section", keys:map[string]*ini.Key{}, keyList:[]string{}, keysHash:map[string]string{}, isRawSection:false, rawBody:""}

Section names:[DEFAULT mysql redis new_section]

调用Section(name)获取以name为名的section,若此section不存在,则自动创建一个section返回.之后调用SectionStrings新的section也会被返回

也可以手动创建一个新的分区

err := cfg.NewSection("new")

子分区

在配置文件中,可以使用占位符%(name)s表示用之前已定义的Keyname的值来替换,s表示值为字符串占用类型

NAME = ini 
VERSION = v1
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s

[package]
CLONE_URL = https://%(IMPORT_PATH)s

[package.sub]

上述文件设置IMPORT_PATH时使用前面定义的NAMEVERSION。在package中设置CLONE_URL时,使用了默认section中定义的IMPORT_PATH

可以在section名中使用.来表示子section。若在package.sub中未找到所需的key就会到package中寻找

package main

import (
	"fmt"
	"gopkg.in/ini.v1"
	"log"
)

func main() {
	config, err := ini.Load("../resources/sub_section.ini")
	if err != nil {
		log.Fatal("Read config error:", err)
	}
	fmt.Println("CLONE_URL:", config.Section("package.sub").Key("CLONE_URL").String())
}

$ go run ./main.go 
CLONE_URL: https://gopkg.in/ini.v1

package.sub中没有CLONE_URL,返回package中的值

保存配置

有时需要将配置写入到文件中。写入配置有两种方式:

  • SaveTo:写入到文件中
  • WriteTo:写入到io.Writer
err = cfg.SaveTo("my.ini")
err = cfg.SaveToIndent("my.ini","\t")

cfg.WriteTo(writer)
cfg.WriteToIndent(writer,"\t")
package main

import (
	"fmt"
	"gopkg.in/ini.v1"
	"os"
)

func main() {
	config := ini.Empty()

	defaultSection := config.Section("")
	defaultSection.NewKey("app_name", "save config")
	defaultSection.NewKey("log_level", "DEBUG")

	mysqlSec, err := config.NewSection("mysql")
	if err != nil {
		fmt.Println("create mysql section error:", err)
	}
	mysqlSec.NewKey("ip", "127.0.0.1")
	mysqlSec.NewKey("port", "3306")

	redisSec, err := config.NewSection("reids")
	if err != nil {
		fmt.Println("create redis section error :", err)
	}
	redisSec.NewKey("ip", "127.0.0.1")
	redisSec.NewKey("port", "6379")

	err = config.SaveTo("../resources/saved-config.ini")
	if err != nil {
		fmt.Println("save config error :", err)
	}

	err = config.SaveToIndent("../resources/saved-config-pretty.ini", " ")
	if err != nil {
		fmt.Println("save config error:", err)
	}

	config.WriteTo(os.Stdout)
	fmt.Println()
	config.WriteToIndent(os.Stdout, " ")

}

saved-config.ini:

app_name  = save config
log_level = DEBUG

[mysql]
ip   = 127.0.0.1
port = 3306

[reids]
ip   = 127.0.0.1
port = 6379

saved-config-pretty.ini:

app_name  = save config
log_level = DEBUG

[mysql]
 ip   = 127.0.0.1
 port = 3306

[reids]
 ip   = 127.0.0.1
 port = 6379

[SaveTo/WriteTo]Indent方法会对Section下的key-Value添加缩进

Section和struct映射

定义结构变量,加载完配置文件后,调用MapTo将配置项赋值到结构变量的字段中

package main

import (
	"fmt"
	"gopkg.in/ini.v1"
	"log"
)

type Config struct {
	AppName  string `ini:"app_name"`
	LogLevel string `ini:"log_level"`

	MySql MysqlConfig `ini:"mysql"'`
	Redis RedisConfig `ini:"redis"`
}

type MysqlConfig struct {
	IP       string `ini:"ip"`
	Port     int    `ini:"port"`
	User     string `ini:"user"`
	Password string `ini:"password"`
	Database string `ini:"database"`
}

type RedisConfig struct {
	IP   string `ini:"ip"`
	Port int    `ini:"port"`
}

func main() {
	config, err := ini.Load("../resources/config.ini")
	if err != nil {
		log.Fatal("load config error:", err)
	}
	c := Config{}
	config.MapTo(&c)
	fmt.Printf("%#v", c)
}

MapTo内部使用了反射,所以结构字段必须是导出的。若字段名和键名不同,则需要在结构标签中指定对应的键名。

可以使用ini.MapTo(&c,"my.ini")将加载和映射合并

err := ini.MapTo(&c,"my.ini")

也可以仅映射一个分区:

mysqlConfig := MysqlConfig{}
err := config.Section("mysql").MapTo(&mysqlConfig)

还可以通过结构体生成配置:

	config2 := ini.Empty()
	c1 := Config{
		AppName:  "map_to_struct",
		LogLevel: "DEBUG",
		MySql: MysqlConfig{
			IP:   "127.0.0.1",
			Port: 3306,
			User: "root",
		},
		Redis: RedisConfig{
			IP: "127.0.0.1",
		},
	}

	err = ini.ReflectFrom(config2, &c1)
	if err != nil {
		log.Fatal("load from struct error")
	}
	err = config2.SaveTo("../resources/load_from_Struct.ini")
	if err != nil {
		log.Fatal("save config error", err)
	}

其他

go-ini还有其他的高级特性,可以参考官方文档open in new window

参考

  1. go-iniopen in new window gitHub repo
  2. go-iniopen in new window godoc
  3. go-iniopen in new window darjun blog
上次编辑于:
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.2