Slim

ini - INI 配置文件解析库

ini 是一个功能强大的 INI 配置文件解析库,基于 go-ini 进行改进,主要增强了环境变量注入功能。支持多种数据源、类型转换、变量引用、环境变量替换等高级特性。

安装

go get -u go-slim.dev/ini

快速开始

基础用法

package mainimport (    "fmt"    "log"    "go-slim.dev/ini")func main() {    // 创建 INI 管理器并加载文件    cfg := ini.New(ini.Options{})    if err := cfg.Append("config.ini"); err != nil {        log.Fatal(err)    }    // 读取配置值    section := cfg.Section("database")    host := section.String("host")    port := section.MustInt("port", 3306)    fmt.Printf("Database: %s:%d\n", host, port)}

INI 文件示例

# config.ini# 应用配置[app]name = MyAppdebug = trueport = 8080# 数据库配置[database]host = localhostport = 3306username = rootpassword = secretmax_connections = 100# Redis 配置[redis]host = localhostport = 6379database = 0

核心概念

1. Manager(管理器)

Manager 是 INI 配置的核心管理对象,负责加载、解析和管理所有的配置数据。

2. Section(节)

Section 代表 INI 文件中的一个配置节(用 [section_name] 标记)。

3. Key(键)

Key 代表配置节中的一个键值对。

层级结构

Manager  └── Section 1      ├── Key 1      ├── Key 2      └── ...  └── Section 2      ├── Key 1      └── ...

核心功能

1. 创建管理器

New - 创建新的管理器

func New(opts Options) *Manager

说明

创建一个新的 INI 管理器,使用指定的选项。

参数

  • opts:配置选项(见 Options 说明)

示例

// 使用默认选项cfg := ini.New(ini.Options{})// 自定义选项cfg := ini.New(ini.Options{    Insensitive: true,              // 键名不区分大小写    AllowBooleanKeys: true,         // 允许布尔类型键    IgnoreInlineComment: false,     // 不忽略行内注释})

2. 加载数据源

Append - 追加数据源

func (m *Manager) Append(source any, others ...any) error

说明

追加一个或多个数据源并自动重新加载。支持多种数据源类型。

支持的数据源类型

  • string:文件路径
  • []byte:字节切片
  • io.Reader:读取器
  • io.ReadCloser:可关闭的读取器
  • DataSource:自定义数据源接口
  • func() (io.ReadCloser, error):数据源工厂函数

示例

cfg := ini.New(ini.Options{})// 从文件加载cfg.Append("config.ini")// 从多个文件加载cfg.Append("base.ini", "production.ini", "local.ini")// 从字节切片加载data := []byte(`[app]name = MyAppport = 8080`)cfg.Append(data)// 从 io.Reader 加载file, _ := os.Open("config.ini")cfg.Append(file)

Batch - 批量操作

func (m *Manager) Batch(fn func(m *Manager) error) error

说明

批量执行多个操作,期间不会触发自动重新加载,所有操作完成后统一加载。

示例

cfg := ini.New(ini.Options{})err := cfg.Batch(func(m *ini.Manager) error {    // 批量加载多个文件,只在最后重新加载一次    if err := m.Append("base.ini"); err != nil {        return err    }    if err := m.Append("override.ini"); err != nil {        return err    }    return nil})

Reload - 重新加载

func (m *Manager) Reload() error

说明

重新加载并解析所有数据源。会清空现有数据并重新解析。

示例

// 修改配置文件后重新加载if err := cfg.Reload(); err != nil {    log.Fatal(err)}

3. Section 操作

Section - 获取节

func (m *Manager) Section(name string) *Section

说明

获取指定名称的节。如果节不存在,返回一个零值节(不会报错)。

示例

// 获取节dbSection := cfg.Section("database")appSection := cfg.Section("app")// 默认节(文件开头没有节名的部分)defaultSection := cfg.Section("")

GetSection - 获取节(带错误检查)

func (m *Manager) GetSection(name string) (*Section, error)

说明

获取指定名称的节。如果节不存在,返回错误。

示例

section, err := cfg.GetSection("database")if err != nil {    log.Printf("Section not found: %v", err)    return}

HasSection - 检查节是否存在

func (m *Manager) HasSection(name string) bool

说明

检查指定的节是否存在。

示例

if cfg.HasSection("database") {    // 数据库配置存在}

NewSection - 创建新节

func (m *Manager) NewSection(name string) *Section

说明

创建一个新的节。如果节已存在,返回现有的节。

示例

// 创建新节section := cfg.NewSection("cache")section.NewKey("driver", "redis")section.NewKey("host", "localhost")

4. Key 操作

Key - 获取键

func (s *Section) Key(name string) *Key

说明

获取指定名称的键。如果键不存在,返回一个零值键(不会报错)。

示例

section := cfg.Section("database")hostKey := section.Key("host")portKey := section.Key("port")

GetKey - 获取键(带错误检查)

func (s *Section) GetKey(name string) (*Key, error)

说明

获取指定名称的键。如果键不存在,返回错误。

支持子节查找:如果当前节中找不到键,会向上查找父节。

示例

section := cfg.Section("database")key, err := section.GetKey("host")if err != nil {    log.Printf("Key not found: %v", err)    return}

HasKey - 检查键是否存在

func (s *Section) HasKey(name string) bool

示例

section := cfg.Section("database")if section.HasKey("password") {    // 密码配置存在}

NewKey - 创建新键

func (s *Section) NewKey(name, value string) *Key

说明

在节中创建一个新的键值对。如果键已存在,返回现有的键。

示例

section := cfg.NewSection("app")section.NewKey("name", "MyApp")section.NewKey("version", "1.0.0")

Keys - 获取所有键

func (s *Section) Keys() []*Key

说明

返回节中所有的键。

示例

section := cfg.Section("database")for _, key := range section.Keys() {    fmt.Printf("%s = %s\n", key.Name(), key.String())}

5. 值读取 - 基本类型

String - 获取字符串值

func (s *Section) String(name string) stringfunc (k *Key) String() string

示例

// 从 Section 读取host := cfg.Section("database").String("host")// 从 Key 读取key := cfg.Section("database").Key("host")value := key.String()

MustString - 获取字符串值(带默认值)

func (s *Section) MustString(name string, defaultVal ...string) stringfunc (k *Key) MustString(defaultVal string) string

示例

// 如果键不存在或为空,返回默认值host := cfg.Section("database").MustString("host", "localhost")

Int/Int64/Uint/Uint64 - 获取整数值

func (s *Section) Int(name string) (int, error)func (s *Section) Int64(name string) (int64, error)func (s *Section) Uint(name string) (uint, error)func (s *Section) Uint64(name string) (uint64, error)

示例

port, err := cfg.Section("database").Int("port")if err != nil {    log.Printf("Invalid port: %v", err)}maxConns, _ := cfg.Section("database").Uint("max_connections")

MustInt/MustInt64/MustUint/MustUint64 - 获取整数值(带默认值)

func (s *Section) MustInt(name string, defaultVal ...int) intfunc (s *Section) MustInt64(name string, defaultVal ...int64) int64func (s *Section) MustUint(name string, defaultVal ...uint) uintfunc (s *Section) MustUint64(name string, defaultVal ...uint64) uint64

示例

// 转换失败或不存在时返回默认值port := cfg.Section("app").MustInt("port", 8080)timeout := cfg.Section("server").MustInt64("timeout", 30)maxSize := cfg.Section("upload").MustUint("max_size", 1024)

Float64 - 获取浮点数值

func (s *Section) Float64(name string) (float64, error)func (s *Section) MustFloat64(name string, defaultVal ...float64) float64

示例

rate, err := cfg.Section("app").Float64("tax_rate")ratio := cfg.Section("app").MustFloat64("compression_ratio", 0.8)

Bool - 获取布尔值

func (s *Section) Bool(name string) (bool, error)func (s *Section) MustBool(name string, defaultVal ...bool) bool

支持的布尔值

  • 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”

示例

debug, err := cfg.Section("app").Bool("debug")verbose := cfg.Section("app").MustBool("verbose", false)

Duration - 获取时间间隔

func (s *Section) Duration(name string) (time.Duration, error)func (s *Section) MustDuration(name string, defaultVal ...time.Duration) time.Duration

示例

// config.ini// [server]// timeout = 30s// interval = 5mtimeout, err := cfg.Section("server").Duration("timeout")interval := cfg.Section("server").MustDuration("interval", 1*time.Minute)

Time - 获取时间值

func (s *Section) Time(name string) (time.Time, error)func (s *Section) TimeFormat(name string, format string) (time.Time, error)func (s *Section) MustTime(name string, defaultVal ...time.Time) time.Timefunc (s *Section) MustTimeFormat(name string, format string, defaultVal ...time.Time) time.Time

示例

// config.ini// [app]// created_at = 2023-12-25T10:30:45Z// updated_at = 2023/12/25 10:30:45// RFC3339 格式createdAt, err := cfg.Section("app").Time("created_at")// 自定义格式updatedAt, err := cfg.Section("app").TimeFormat("updated_at", "2006/01/02 15:04:05")// 带默认值defaultTime := time.Now()lastLogin := cfg.Section("user").MustTime("last_login", defaultTime)

6. 列表值读取

Strings - 获取字符串列表

func (s *Section) Strings(name string, delim string) []stringfunc (k *Key) Strings(delim string) []string

说明

使用指定的分隔符分割值,返回字符串列表。

示例

// config.ini// [app]// allowed_origins = http://localhost:3000,https://example.com,https://api.example.com// tags = go, web, apiorigins := cfg.Section("app").Strings("allowed_origins", ",")// []string{"http://localhost:3000", "https://example.com", "https://api.example.com"}tags := cfg.Section("app").Strings("tags", ",")// []string{"go", "web", "api"} (自动 trim 空格)

Ints/Int64s/Uints/Uint64s - 获取整数列表

func (s *Section) Ints(name string, delim string) []intfunc (s *Section) Int64s(name string, delim string) []int64func (s *Section) Uints(name string, delim string) []uintfunc (s *Section) Uint64s(name string, delim string) []uint64

说明

分割值并转换为整数列表。无效的值会被转换为零值。

示例

// config.ini// [app]// ports = 8080,8081,8082// retry_delays = 1,2,5,10ports := cfg.Section("app").Ints("ports", ",")// []int{8080, 8081, 8082}delays := cfg.Section("app").Int64s("retry_delays", ",")// []int64{1, 2, 5, 10}

ValidInts/ValidInt64s - 获取有效整数列表

func (s *Section) ValidInts(name string, delim string) []intfunc (s *Section) ValidInt64s(name string, delim string) []int64

说明

分割值并转换为整数列表。无效的值会被跳过(不包含在结果中)。

示例

// config.ini// [app]// ports = 8080,invalid,8081,8082ports := cfg.Section("app").Ints("ports", ",")// []int{8080, 0, 8081, 8082} (invalid 转换为 0)validPorts := cfg.Section("app").ValidInts("ports", ",")// []int{8080, 8081, 8082} (invalid 被跳过)

StrictInts/StrictInt64s - 获取严格整数列表

func (s *Section) StrictInts(name string, delim string) ([]int, error)func (s *Section) StrictInt64s(name string, delim string) ([]int64, error)

说明

分割值并转换为整数列表。遇到第一个无效值时返回错误。

示例

// config.ini// [app]// ports = 8080,8081,8082// bad_ports = 8080,invalid,8082ports, err := cfg.Section("app").StrictInts("ports", ",")// []int{8080, 8081, 8082}, nilbadPorts, err := cfg.Section("app").StrictInts("bad_ports", ",")// nil, error (遇到 "invalid" 时返回错误)

Float64s/Bools/Times - 其他类型列表

// 浮点数列表func (s *Section) Float64s(name string, delim string) []float64func (s *Section) ValidFloat64s(name string, delim string) []float64func (s *Section) StrictFloat64s(name string, delim string) ([]float64, error)// 布尔值列表func (s *Section) Bools(name string, delim string) []boolfunc (s *Section) ValidBools(name string, delim string) []boolfunc (s *Section) StrictBools(name string, delim string) ([]bool, error)// 时间列表func (s *Section) Times(name string, delim string) []time.Timefunc (s *Section) TimesFormat(name string, format, delim string) []time.Timefunc (s *Section) ValidTimes(name string, delim string) []time.Timefunc (s *Section) StrictTimes(name string, delim string) ([]time.Time, error)

示例

// config.ini// [app]// rates = 0.1,0.2,0.3// features = true,false,true,false// timestamps = 2023-01-01T00:00:00Z,2023-06-01T00:00:00Zrates := cfg.Section("app").Float64s("rates", ",")features := cfg.Section("app").Bools("features", ",")timestamps := cfg.Section("app").Times("timestamps", ",")

7. 值验证和范围检查

In - 检查值是否在候选列表中

func (s *Section) In(name string, defaultVal string, candidates []string) stringfunc (s *Section) InInt(name string, defaultVal int, candidates []int) intfunc (s *Section) InFloat64(name string, defaultVal float64, candidates []float64) float64

说明

检查值是否在给定的候选列表中。如果不在,返回默认值。

示例

// config.ini// [app]// log_level = debug// port = 8080// 只允许特定的日志级别logLevel := cfg.Section("app").In("log_level", "info",    []string{"debug", "info", "warn", "error"})// 返回 "debug"invalidLevel := cfg.Section("app").In("invalid_level", "info",    []string{"debug", "info", "warn", "error"})// 键不存在,返回默认值 "info"// 只允许特定的端口port := cfg.Section("app").InInt("port", 8080,    []int{8080, 8081, 8082})// 返回 8080

Range - 范围检查

func (s *Section) RangeInt(name string, defaultVal, min, max int) intfunc (s *Section) RangeInt64(name string, defaultVal, min, max int64) int64func (s *Section) RangeFloat64(name string, defaultVal, min, max float64) float64func (s *Section) RangeTime(name string, defaultVal, min, max time.Time) time.Time

说明

检查值是否在给定范围内(包含边界)。如果超出范围,返回默认值。

示例

// config.ini// [server]// port = 8080// workers = 4// rate_limit = 0.95// 端口必须在 1024-65535 之间port := cfg.Section("server").RangeInt("port", 8080, 1024, 65535)// 返回 8080(在范围内)badPort := cfg.Section("server").RangeInt("bad_port", 8080, 1024, 65535)// 假设 bad_port = 100,返回默认值 8080(超出范围)// 工作线程数必须在 1-16 之间workers := cfg.Section("server").RangeInt("workers", 4, 1, 16)// 速率限制必须在 0.0-1.0 之间rateLimit := cfg.Section("server").RangeFloat64("rate_limit", 0.8, 0.0, 1.0)

Validate - 自定义验证

func (s *Section) Validate(name string, fn func(string) string) stringfunc (k *Key) Validate(fn func(string) string) string

说明

使用自定义函数验证并可能修改键值。

示例

// config.ini// [app]// url = HTTP://EXAMPLE.COM/API// 转换为小写url := cfg.Section("app").Validate("url", func(value string) string {    return strings.ToLower(value)})// 返回 "http://example.com/api"// 确保以斜杠结尾apiBase := cfg.Section("app").Validate("api_base", func(value string) string {    if !strings.HasSuffix(value, "/") {        return value + "/"    }    return value})

8. 变量引用和环境变量

变量引用

INI 文件支持在值中引用其他键的值,使用 %(key)s 语法。

示例

# config.ini[paths]root = /var/wwwdata = %(root)s/datalogs = %(root)s/logscache = %(root)s/cache[database]host = localhostport = 3306dsn = %(host)s:%(port)s
cfg := ini.New(ini.Options{})cfg.Append("config.ini")// 自动解析引用dataPath := cfg.Section("paths").String("data")// 返回 "/var/www/data"logsPath := cfg.Section("paths").String("logs")// 返回 "/var/www/logs"dsn := cfg.Section("database").String("dsn")// 返回 "localhost:3306"

跨节引用

如果当前节中找不到引用的键,会自动查找默认节(无名节):

# config.ini# 默认节root = /var/www[app]data_dir = %(root)s/data
dataDir := cfg.Section("app").String("data_dir")// 返回 "/var/www/data" (root 从默认节获取)

环境变量注入

这是 go-slim.dev/ini 相对于原版 go-ini 的主要增强功能。

支持两种语法注入环境变量:

语法 1:${VAR||default} - 空值回退

当环境变量不存在或为空字符串时,使用默认值。

语法 2:${VAR??default} - 不存在回退

只有当环境变量不存在时,才使用默认值(空字符串被视为有效值)。

示例

# config.ini[database]# 从环境变量读取,不存在时使用默认值host = ${DB_HOST||localhost}port = ${DB_PORT||3306}username = ${DB_USER||root}password = ${DB_PASSWORD}[app]# 强制回退:即使环境变量存在但为空,也使用默认值api_key = ${API_KEY||default_key}# 弱回退:只有不存在才使用默认值feature_flag = ${FEATURE_FLAG??true}
// 设置环境变量os.Setenv("DB_HOST", "prod-db.example.com")os.Setenv("DB_PORT", "5432")os.Setenv("FEATURE_FLAG", "")  // 空字符串cfg := ini.New(ini.Options{})cfg.Append("config.ini")// 读取配置host := cfg.Section("database").String("host")// 返回 "prod-db.example.com" (从环境变量)port := cfg.Section("database").MustInt("port", 3306)// 返回 5432 (从环境变量)username := cfg.Section("database").String("username")// 返回 "root" (环境变量未设置,使用默认值)// || vs ??apiKey := cfg.Section("app").String("api_key")// 如果 API_KEY 为空字符串,返回 "default_key"featureFlag := cfg.Section("app").String("feature_flag")// 返回 "" (环境变量存在但为空,?? 不使用默认值)

默认值中的引号

默认值可以使用单引号或双引号包裹:

[app]message = ${WELCOME_MSG||'Hello, World!'}api_url = ${API_URL||"https://api.example.com"}

9. 子节(Child Sections)

子节使用点号分隔符创建层级关系。

示例

# config.ini[server]host = 0.0.0.0[server.http]port = 8080timeout = 30s[server.https]port = 8443timeout = 60scert = /path/to/cert.pem
cfg := ini.New(ini.Options{    ChildSectionDelimiter: ".",  // 默认就是 "."})cfg.Append("config.ini")// 访问子节httpSection := cfg.Section("server.http")httpPort := httpSection.MustInt("port", 80)httpsSection := cfg.Section("server.https")httpsPort := httpsSection.MustInt("port", 443)// 子节会继承父节的键// 如果 server.http 中没有 host,会从 server 中查找host := httpSection.String("host")// 返回 "0.0.0.0" (从父节 server 继承)

获取父节

httpsSection := cfg.Section("server.https")parentSection, hasParent := httpsSection.Parent()if hasParent {    fmt.Println(parentSection.Name())  // "server"}

Options 配置选项

type Options struct {    // Loose 表示解析器是否忽略不存在的文件    Loose bool    // Insensitive 表示是否强制所有节名和键名小写    Insensitive bool    // InsensitiveSections 表示是否强制所有节名小写    InsensitiveSections bool    // InsensitiveKeys 表示是否强制所有键名小写    InsensitiveKeys bool    // IgnoreContinuation 表示是否忽略续行    IgnoreContinuation bool    // IgnoreInlineComment 表示是否忽略行内注释    IgnoreInlineComment bool    // AllowBooleanKeys 表示是否允许布尔类型的键(值为 true)    // 主要用于 my.cnf 类型的配置文件    AllowBooleanKeys bool    // AllowPythonMultilineValues 表示是否允许 Python 风格的多行值    AllowPythonMultilineValues bool    // SpaceBeforeInlineComment 表示注释符号前是否需要空格    SpaceBeforeInlineComment bool    // UnescapeValueDoubleQuotes 表示是否反转义双引号中的引号    UnescapeValueDoubleQuotes bool    // UnescapeValueCommentSymbols 表示是否反转义注释符号    UnescapeValueCommentSymbols bool    // KeyValueDelimiters 键值分隔符,默认 "=:"    KeyValueDelimiters string    // ChildSectionDelimiter 子节分隔符,默认 "."    ChildSectionDelimiter string    // PreserveSurroundedQuote 表示是否保留值周围的引号    PreserveSurroundedQuote bool    // ReaderBufferSize 读取器缓冲区大小(字节)    ReaderBufferSize int    // AllowNonUniqueSections 表示是否允许同名节    AllowNonUniqueSections bool    // AllowDuplicateShadowValues 表示是否允许重复的影子值    AllowDuplicateShadowValues bool    // Mutex 并发锁(可自定义)    Mutex Mutex    // Transformer 自定义值转换器    Transformer ValueTransformer}

常用配置示例

// 宽松模式:忽略不存在的文件cfg := ini.New(ini.Options{    Loose: true,})// 不区分大小写cfg := ini.New(ini.Options{    Insensitive: true,})// 允许布尔键(MySQL 配置风格)cfg := ini.New(ini.Options{    AllowBooleanKeys: true,})// 忽略行内注释cfg := ini.New(ini.Options{    IgnoreInlineComment: true,})// 自定义分隔符cfg := ini.New(ini.Options{    KeyValueDelimiters: "=",  // 只允许 = 作为分隔符    ChildSectionDelimiter: "::",  // 使用 :: 作为子节分隔符})

使用场景

1. 应用配置管理

// config.ini// [app]// name = MyApp// version = 1.0.0// debug = ${DEBUG||false}// port = ${PORT||8080}//// [database]// host = ${DB_HOST||localhost}// port = ${DB_PORT||3306}// name = ${DB_NAME||mydb}// user = ${DB_USER||root}// password = ${DB_PASSWORD}type Config struct {    App      AppConfig    Database DatabaseConfig}type AppConfig struct {    Name    string    Version string    Debug   bool    Port    int}type DatabaseConfig struct {    Host     string    Port     int    Name     string    User     string    Password string}func LoadConfig() (*Config, error) {    cfg := ini.New(ini.Options{})    if err := cfg.Append("config.ini"); err != nil {        return nil, err    }    config := &Config{        App: AppConfig{            Name:    cfg.Section("app").String("name"),            Version: cfg.Section("app").String("version"),            Debug:   cfg.Section("app").MustBool("debug", false),            Port:    cfg.Section("app").MustInt("port", 8080),        },        Database: DatabaseConfig{            Host:     cfg.Section("database").String("host"),            Port:     cfg.Section("database").MustInt("port", 3306),            Name:     cfg.Section("database").String("name"),            User:     cfg.Section("database").String("user"),            Password: cfg.Section("database").String("password"),        },    }    return config, nil}

2. 多环境配置

// 加载基础配置 + 环境特定配置cfg := ini.New(ini.Options{Loose: true})// 批量加载err := cfg.Batch(func(m *ini.Manager) error {    // 基础配置    m.Append("config.ini")    // 环境特定配置    env := os.Getenv("APP_ENV")    if env != "" {        m.Append(fmt.Sprintf("config.%s.ini", env))    }    // 本地覆盖配置    m.Append("config.local.ini")    return nil})

3. 服务配置与环境变量结合

// config.ini// [redis]// host = ${REDIS_HOST||localhost}// port = ${REDIS_PORT||6379}// password = ${REDIS_PASSWORD}// database = ${REDIS_DB||0}// max_retries = 3// pool_size = ${REDIS_POOL_SIZE||10}func NewRedisClient() (*redis.Client, error) {    cfg := ini.New(ini.Options{})    if err := cfg.Append("config.ini"); err != nil {        return nil, err    }    section := cfg.Section("redis")    client := redis.NewClient(&redis.Options{        Addr:     fmt.Sprintf("%s:%d",            section.String("host"),            section.MustInt("port", 6379)),        Password: section.String("password"),        DB:       section.MustInt("database", 0),        MaxRetries: section.MustInt("max_retries", 3),        PoolSize:   section.MustInt("pool_size", 10),    })    return client, nil}

4. 动态配置重载

type ConfigManager struct {    cfg      *ini.Manager    watchers []func(*ini.Manager)    mu       sync.RWMutex}func NewConfigManager(filename string) (*ConfigManager, error) {    cfg := ini.New(ini.Options{})    if err := cfg.Append(filename); err != nil {        return nil, err    }    return &ConfigManager{        cfg:      cfg,        watchers: make([]func(*ini.Manager), 0),    }, nil}func (cm *ConfigManager) Reload() error {    cm.mu.Lock()    defer cm.mu.Unlock()    if err := cm.cfg.Reload(); err != nil {        return err    }    // 通知所有观察者    for _, watcher := range cm.watchers {        watcher(cm.cfg)    }    return nil}func (cm *ConfigManager) Watch(fn func(*ini.Manager)) {    cm.mu.Lock()    defer cm.mu.Unlock()    cm.watchers = append(cm.watchers, fn)}func (cm *ConfigManager) GetInt(section, key string, defaultVal int) int {    cm.mu.RLock()    defer cm.mu.RUnlock()    return cm.cfg.Section(section).MustInt(key, defaultVal)}

5. 复杂配置验证

// config.ini// [server]// host = 0.0.0.0// port = 8080// workers = 4// timeout = 30s//// [limits]// max_connections = 1000// rate_limit = 100// request_size = 10485760func ValidateConfig(cfg *ini.Manager) error {    // 验证端口范围    port := cfg.Section("server").MustInt("port", 8080)    if port < 1024 || port > 65535 {        return fmt.Errorf("invalid port: %d (must be 1024-65535)", port)    }    // 验证工作线程数    workers := cfg.Section("server").MustInt("workers", 4)    if workers < 1 || workers > 256 {        return fmt.Errorf("invalid workers: %d (must be 1-256)", workers)    }    // 验证超时时间    timeout, err := cfg.Section("server").Duration("timeout")    if err != nil || timeout < time.Second || timeout > 5*time.Minute {        return fmt.Errorf("invalid timeout: must be 1s-5m")    }    // 验证限制配置    maxConns := cfg.Section("limits").MustInt("max_connections", 100)    if maxConns < 1 {        return fmt.Errorf("max_connections must be positive")    }    return nil}

API 参考

Manager 方法

方法说明
New(opts Options) *Manager创建新管理器
Append(source any, others ...any) error追加数据源
Batch(fn func(m *Manager) error) error批量操作
Reload() error重新加载
Section(name string) *Section获取节
GetSection(name string) (*Section, error)获取节(带错误)
HasSection(name string) bool检查节是否存在
NewSection(name string) *Section创建新节

Section 方法

方法说明
Name() string获取节名
Parent() (*Section, bool)获取父节
Key(name string) *Key获取键
GetKey(name string) (*Key, error)获取键(带错误)
HasKey(name string) bool检查键是否存在
NewKey(name, value string) *Key创建新键
Keys() []*Key获取所有键

值读取方法(Section/Key)

方法说明
String(name string) string字符串值
MustString(name, defaultVal string) string带默认值的字符串
Int/Int64/Uint/Uint64(name string) (T, error)整数值
MustInt/MustInt64/...(name string, defaultVal T) T带默认值的整数
Float64(name string) (float64, error)浮点数值
MustFloat64(name, defaultVal float64) float64带默认值的浮点数
Bool(name string) (bool, error)布尔值
MustBool(name string, defaultVal bool) bool带默认值的布尔值
Duration(name string) (time.Duration, error)时间间隔
Time/TimeFormat(name, format string) (time.Time, error)时间值

列表方法

方法说明
Strings(name, delim string) []string字符串列表
Ints/Int64s/Uints/Uint64s(name, delim string) []T整数列表(无效值为0)
ValidInts/ValidInt64s/...(name, delim string) []T有效整数列表(跳过无效值)
StrictInts/StrictInt64s/...(name, delim string) ([]T, error)严格整数列表(遇错返回)
Float64s/Bools/Times(name, delim string) []T其他类型列表

验证方法

方法说明
In(name, defaultVal string, candidates []string) string候选值检查
InInt/InFloat64/...(name, defaultVal, candidates) T类型化候选值检查
RangeInt/RangeFloat64/...(name, defaultVal, min, max) T范围检查
Validate(name, fn func(string) string) string自定义验证

注意事项

1. 环境变量语法差异

理解 ||?? 的区别:

# || 空值回退:环境变量不存在或为空时使用默认值value1 = ${VAR||default}# ?? 不存在回退:只有环境变量不存在时才使用默认值value2 = ${VAR??default}
// 设置空字符串os.Setenv("VAR", "")cfg.Section("").String("value1")  // "default" (|| 检查空值)cfg.Section("").String("value2")  // "" (?? 不检查空值)

2. 默认节

没有节名的键值对属于默认节(空字符串名称):

# 默认节root = /var/wwwdebug = true[app]name = MyApp
// 访问默认节root := cfg.Section("").String("root")debug := cfg.Section("").MustBool("debug", false)

3. 键名大小写

根据 Options 设置,键名可能被转换为小写:

// 区分大小写(默认)cfg := ini.New(ini.Options{})cfg.Section("app").String("UserName")  // 必须完全匹配// 不区分大小写cfg := ini.New(ini.Options{Insensitive: true})cfg.Section("app").String("username")  // 可以匹配 UserName、USERNAME 等cfg.Section("app").String("USERNAME")  // 同样有效

4. 多数据源合并

后加载的数据源会覆盖先加载的同名键:

cfg := ini.New(ini.Options{})cfg.Append("base.ini")       // port = 8080cfg.Append("override.ini")   // port = 9000port := cfg.Section("app").MustInt("port")// 返回 9000 (override.ini 覆盖了 base.ini)

5. 类型转换失败

使用 Must* 系列函数时,转换失败会使用默认值而不是报错:

// config.ini: port = invalidport, err := cfg.Section("app").Int("port")// err != nil, port = 0port := cfg.Section("app").MustInt("port", 8080)// port = 8080 (转换失败,使用默认值)

6. 变量引用循环

避免循环引用,否则会达到最大深度(99层):

# 错误示例:循环引用[bad]a = %(b)sb = %(a)s

7. 线程安全

Manager 内部使用互斥锁保护并发访问。可以自定义 Mutex:

cfg := ini.New(ini.Options{    Mutex: &sync.RWMutex{},  // 使用读写锁})

8. 子节继承

子节会从父节继承键,但不会递归继承:

[a]key1 = value1[a.b]key2 = value2[a.b.c]key3 = value3
// a.b.c 可以访问 a.b 的键,但不能直接访问 a 的键cfg.Section("a.b.c").String("key2")  // OK (从 a.b 继承)cfg.Section("a.b.c").String("key1")  // 找不到 (不递归继承)

最佳实践

1. 使用环境变量覆盖敏感配置

# config.ini - 提交到版本控制[database]host = ${DB_HOST||localhost}port = ${DB_PORT||3306}name = ${DB_NAME||mydb}user = ${DB_USER||root}# 密码从环境变量读取,无默认值password = ${DB_PASSWORD}[api]# API 密钥必须从环境变量提供key = ${API_KEY}secret = ${API_SECRET}

2. 分层配置文件

// 基础 -> 环境 -> 本地cfg := ini.New(ini.Options{Loose: true})cfg.Batch(func(m *ini.Manager) error {    m.Append("config.ini")              // 基础配置    m.Append("config.production.ini")   // 生产环境    m.Append("config.local.ini")        // 本地覆盖(不提交)    return nil})

3. 配置验证

func LoadAndValidateConfig() (*ini.Manager, error) {    cfg := ini.New(ini.Options{})    if err := cfg.Append("config.ini"); err != nil {        return nil, err    }    // 验证必需配置    required := map[string][]string{        "database": {"host", "port", "name", "user", "password"},        "redis":    {"host", "port"},    }    for section, keys := range required {        for _, key := range keys {            if !cfg.Section(section).HasKey(key) {                return nil, fmt.Errorf("missing required config: %s.%s", section, key)            }        }    }    return cfg, nil}

4. 使用结构化配置

type Config struct {    Server   ServerConfig    Database DatabaseConfig    Redis    RedisConfig}func (c *Config) Load(cfg *ini.Manager) error {    c.Server.Load(cfg.Section("server"))    c.Database.Load(cfg.Section("database"))    c.Redis.Load(cfg.Section("redis"))    return c.Validate()}func (c *Config) Validate() error {    // 集中验证逻辑    return nil}

5. 提供配置模板

# config.example.ini - 配置模板# 复制为 config.ini 并填写实际值[app]name = MyAppdebug = ${DEBUG||false}port = ${PORT||8080}[database]host = ${DB_HOST||localhost}port = ${DB_PORT||3306}name = ${DB_NAME||mydb}user = ${DB_USER||root}password = ${DB_PASSWORD}  # 必填:数据库密码

相关链接

切换 选择 esc 关闭搜索