go-ini入門教程

大雜草發表於2021-03-18

go-ini入門教程

go-ini簡介

Package ini provides INI file read and write functionality in Go.

在實際開發時,配置資訊一般不會在程式碼裡硬編碼,通常是放在配置檔案裡,或者資料庫、快取裡。今天介紹的 go-ini 就是一個讀寫 ini檔案的庫。

配置檔案格式有很多,常用的有 jsonxmlini。其中 ini 是以節(section)和鍵(key)構成,如下所示:

#debug or release
RUN_MODE = debug

[app]
PAGE_SIZE = 10

快速使用

1、下載安裝

使用一個特定版本:

$ go get gopkg.in/ini.v1

使用最新版本:

$ go get github.com/go-ini/ini

如需更新請新增 -u 選項。

2、建立兩個檔案(my.inimain.go),這裡選擇放在 /home/ini-sample 目錄。

$ mkdir -p /home/ini-sample
$ cd /home/ini-sample
$ touch my.ini main.go
$ tree .
.
├── main.go
└── my.ini

0 directories, 2 files

3、編譯 my.ini 檔案並輸入以下內容:

#debug or release
RUN_MODE = debug

[app]
PAGE_SIZE = 10

[server]
HTTP_PORT = 8000
READ_TIMEOUT = 60
WRITE_TIMEOUT = 60

[database]
TYPE = mysql
USER = 資料庫賬號
PASSWORD = 資料庫密碼
#127.0.0.1:3306
HOST = 資料庫IP:資料庫埠號
NAME = blog
TABLE_PREFIX = blog_

4、接著編寫 main.go 檔案來操作剛才建立的配置檔案。

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)
    }
    // 典型讀取操作,預設分割槽可以使用空字串表示
    fmt.Println("Run Mode:", cfg.Section("").Key("RUN_MODE").String())
    fmt.Println("Page Size:", cfg.Section("app").Key("PAGE_SIZE").String())
    // 試一試自動型別轉換
    fmt.Printf("Http Port: (%[1]T) %[1]d\n", cfg.Section("server").Key("HTTP_PORT").MustInt(9999))
    // 差不多了,修改某個值然後進行儲存
    cfg.Section("").Key("RUN_MODE").SetValue("release")
    cfg.SaveTo("my.ini.local")
}

5、執行程式,可以看到以下輸出:

$ go run main.go
Run Mode: debug
Page Size: 10
Http Port: (int) 8000

$ cat my.ini.local 
# debug or release
RUN_MODE = release

[app]
PAGE_SIZE = 10

[server]
HTTP_PORT     = 8000
READ_TIMEOUT  = 60
WRITE_TIMEOUT = 60

[database]
TYPE         = mysql
USER         = 資料庫賬號
PASSWORD     = 資料庫密碼
# 127.0.0.1:3306
HOST         = 資料庫IP:資料庫埠號
NAME         = blog
TABLE_PREFIX = blog_

操作分割槽(Section)

獲取指定分割槽:

sec, err := cfg.GetSection("section name")

如果想要獲取預設分割槽,則可以用空字串代替分割槽名:

sec, err := cfg.GetSection("")

操作鍵值(Value)

獲取一個型別為字串(string)的值:

val := cfg.Section("").Key("key name").String()

Must*方法

當我們知道配置的鍵值是哪種格式時,可以使用 Must* 方法來獲取。

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
// 由 Must 開頭的方法名允許接收一個相同型別的引數來作為預設值,
// 當鍵不存在或者轉換失敗時,則會直接返回該預設值。
// 但是,MustString 方法必須傳遞一個預設值。
v = cfg.Section("").Key("String").MustString("default")
v = cfg.Section("").Key("BOOL").MustBool(true)
v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
v = cfg.Section("").Key("INT").MustInt(10)
v = cfg.Section("").Key("INT64").MustInt64(99)
v = cfg.Section("").Key("UINT").MustUint(3)
v = cfg.Section("").Key("UINT64").MustUint64(6)
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339

In*方法

獲取鍵值時設定候選值,可以使用 In* 方法:

v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339

如果獲取到的值不是候選值的任意一個,則會返回預設值,而預設值不需要是候選值中的一員。

結構體與分割槽對映

有時候我們不想逐個獲取值,而是把分割槽內的值放到一個結構體裡,以便使用。

配置檔案:

#debug or release
RUN_MODE = debug

[app]
PAGE_SIZE = 10

[server]
HTTP_PORT = 8000
READ_TIMEOUT = 60
WRITE_TIMEOUT = 60

[database]
TYPE = mysql
USER = 資料庫賬號
PASSWORD = 資料庫密碼
#127.0.0.1:3306
HOST = 資料庫IP:資料庫埠號
NAME = blog
TABLE_PREFIX = blog_

程式碼:

type Database struct {
	Type string
	User string
	Password string
	Host string
	Name string
	TablePrefix string
}

func Setup() {
	Cfg, err := ini.Load("conf/app.ini")
	if err != nil {
		log.Fatalf("Fail to parse 'conf/app.ini': %v", err)
	}

	err = Cfg.Section("database").MapTo(DatabaseSetting)
	if err != nil {
		log.Fatalf("Cfg.MapTo DatabaseSetting err: %v", err)
	}
}

使用心得

通常配置檔案很少變更的,一般使用單例,在 go 裡就是包內變數了,由於只需要讀取一次,所以一般會在 init 方法裡讀取。

另外關於配置熱更新,可以檢視這份文章:熱更新配置檔案

參考資料