# 使用go-ini操作ini

地址：

* [ini.v1 - gopkg.in/ini.v1](https://gopkg.in/ini.v1)
* [go-ini/ini](https://github.com/go-ini/ini)

ini 是 Windows 上常用的配置文件格式。MySQL 的 Windows 版就是使用 ini 格式存储配置的。

## Hello World

创建ini配置文件：

```ini
app_name = awesome web

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

[mysql] # section
ip = 127.0.0.1
port = 3306
user = dj
password = 123456
database = awesome

[redis]
ip = 127.0.0.1
port = 6381
```

使用go-ini读取配置文件：

```go
package main

import (
	"fmt"
	"log"

	"gopkg.in/ini.v1"
)

func main() {
	// 加载ini配置文件
	cfg, err := ini.Load("config/config.ini")
	if err != nil {
		log.Fatal("Fail to read file: ", err)
	}

	// 没有section的kv
	fmt.Println("App Name:", cfg.Section("").Key("app_name").String()) // String返回字符串类型
	fmt.Println("Log Level:", cfg.Section("").Key("log_level").String())

	// mysql section 的 kv
	fmt.Println("MySQL IP:", cfg.Section("mysql").Key("ip").String())
	mysqlPort, err := cfg.Section("mysql").Key("port").Int() // 返回int类型
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("MySQL Port:", mysqlPort)
	fmt.Println("MySQL User:", cfg.Section("mysql").Key("user").String())
	fmt.Println("MySQL Password:", cfg.Section("mysql").Key("password").String())
	fmt.Println("MySQL Database:", cfg.Section("mysql").Key("database").String())

	// redis section 的 kv
	fmt.Println("Redis IP:", cfg.Section("redis").Key("ip").String())
	redisPort, err := cfg.Section("redis").Key("port").Int()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Redis Port:", redisPort)
}
```

提供的类型方法有：

* `String()`
* `Int()` / `Uint` / `Float64`，这些方法都有可能出现类型转换错误，所以会返回一个err

## Must\*便捷方法

如果每次取值都需要进行错误判断，那么代码写起来会非常繁琐。为此，`go-ini`也提供对应的`MustType`（Type 为`Init/Uint/Float64`等）方法，这个方法只返回一个值。同时它接受可变参数，如果类型无法转换，就是用可变参数的第一个值作为默认值：

```go
// 如果redis.port的值不是int类型，MustInt就会返回默认值6381
fmt.Println("redis Port:", cfg.Section("redis").Key("port").MustInt(6381))
```

## 分区操作

```go
package main

import (
	"fmt"
	"log"

	"gopkg.in/ini.v1"
)

func main() {
	// 加载ini配置文件
	cfg, err := ini.Load("config/config.ini")
	if err != nil {
		log.Fatal("Fail to read file: ", err)
	}

	sections := cfg.Sections() // 返回所有分区对象 *[]Section
	fmt.Println(sections) // [0xc000100620 0xc000100770 0xc000100a10]

	names := cfg.SectionStrings() // 获取所有分区的名称
	fmt.Println(names) // [DEFAULT mysql redis]

}
```

## 写入配置

写入配置有两种，一种是写入到文件中，一种是写入到`write`流中：

```go
err = cfg.SaveTo("my.ini")
err = cfg.SaveToIndent("my.ini", "\t")

cfg.WriteTo(writer)
cfg.WriteToIndent(writer, "\t")
```

```go
package main

import (
  "fmt"
  "os"

  "gopkg.in/ini.v1"
)

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

  defaultSection := cfg.Section("")
  defaultSection.NewKey("app_name", "awesome web")
  defaultSection.NewKey("log_level", "DEBUG")

  mysqlSection, err := cfg.NewSection("mysql")
  if err != nil {
    fmt.Println("new mysql section failed:", err)
    return
  }
  mysqlSection.NewKey("ip", "127.0.0.1")
  mysqlSection.NewKey("port", "3306")
  mysqlSection.NewKey("user", "root")
  mysqlSection.NewKey("password", "123456")
  mysqlSection.NewKey("database", "awesome")

  redisSection, err := cfg.NewSection("redis")
  if err != nil {
    fmt.Println("new redis section failed:", err)
    return
  }
  redisSection.NewKey("ip", "127.0.0.1")
  redisSection.NewKey("port", "6381")

  err = cfg.SaveTo("my.ini")
  if err != nil {
    fmt.Println("SaveTo failed: ", err)
  }

  err = cfg.SaveToIndent("my-pretty.ini", "\t")
  if err != nil {
    fmt.Println("SaveToIndent failed: ", err)
  }

  cfg.WriteTo(os.Stdout)
  fmt.Println()
  cfg.WriteToIndent(os.Stdout, "\t")
}
```

## 映射结构体

**注意**：所有的结构体字段必须是导出的，否则不会映射

```go
package main

import (
	"fmt"

	"gopkg.in/ini.v1"
)

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() {
	cfg, err := ini.Load("config/config.ini")
	if err != nil {
		fmt.Println("load my.ini failed: ", err)
	}

	c := Config{}
	cfg.MapTo(&c)

	fmt.Println(c)
}
```

### 映射单个分区

```go
mysqlCfg := MySQLConfig{}
err = cfg.Section("mysql").MapTo(&mysqlCfg)
```

### 根据结构体生成配置

```go
cfg := ini.Empty()

c := Config {
  AppName:     "awesome web",
  LogLevel:     "DEBUG",
  MySQL: MySQLConfig {
    IP:     "127.0.0.1",
    Port:    3306,
    User:    "root",
    Password:"123456",
    Database:"awesome",
  },
  Redis: RedisConfig {
    IP:        "127.0.0.1",
    Port:    6381,
  },
}

err := ini.ReflectFrom(cfg, &c)
if err != nil {
  fmt.Println("ReflectFrom failed: ", err)
  return
}

err = cfg.SaveTo("my-copy.ini")
if err != nil {
  fmt.Println("SaveTo failed: ", err)
  return
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/programming-language/golang/shu-ju-ge-shi-yu-bian-ma/shi-yong-goini-cao-zuo-ini.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
