golang操作ini文件包之go-ini
github:https://github.com/go-ini/ini
ini 是 Windows 上常用的配置文件格式。MySQL 的 Windows 版就是使用 ini 格式存储配置的。 go-ini是 Go 语言中用于操作 ini 文件的第三方库。
本文介绍go-ini库的使用。
快速使用
go-ini 是第三方库,使用前需要安装:
$ go get gopkg.in/ini.v1
也可以使用 GitHub 上的仓库:
$ go get github.com/go-ini/ini
首先,创建一个my.ini配置文件:
app_name = awesome web # possible values: DEBUG, INFO, WARNING, ERROR, FATAL
log_level = DEBUG [mysql]
ip = 127.0.0.1
port = 3306
user = dj
password = 123456
database = awesome [redis]
ip = 127.0.0.1
port = 6381
使用 go-ini 库读取:
package main import (
"fmt"
"log" "gopkg.in/ini.v1"
) func main() {
cfg, err := ini.Load("my.ini")
if err != nil {
log.Fatal("Fail to read file: ", err)
} fmt.Println("App Name:", cfg.Section("").Key("app_name").String())
fmt.Println("Log Level:", cfg.Section("").Key("log_level").String()) fmt.Println("MySQL IP:", cfg.Section("mysql").Key("ip").String())
mysqlPort, err := cfg.Section("mysql").Key("port").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()) 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)
}
在 ini 文件中,每个键值对占用一行,中间使用=隔开。以#开头的内容为注释。ini 文件是以分区(section)组织的。 分区以[name]开始,在下一个分区前结束。所有分区前的内容属于默认分区,如my.ini文件中的app_name和log_level。
使用go-ini读取配置文件的步骤如下:
- 首先调用
ini.Load加载文件,得到配置对象cfg; - 然后以分区名调用配置对象的
Section方法得到对应的分区对象section,默认分区的名字为"",也可以使用ini.DefaultSection; - 以键名调用分区对象的
Key方法得到对应的配置项key对象; - 由于文件中读取出来的都是字符串,
key对象需根据类型调用对应的方法返回具体类型的值使用,如上面的String、MustInt方法。
运行以下程序,得到输出:
App Name: awesome web
Log Level: DEBUG
MySQL IP: 127.0.0.1
MySQL Port: 3306
MySQL User: dj
MySQL Password: 123456
MySQL Database: awesome
Redis IP: 127.0.0.1
Redis Port: 6381
配置文件中存储的都是字符串,所以类型为字符串的配置项不会出现类型转换失败的,故String()方法只返回一个值。 但如果类型为Int/Uint/Float64这些时,转换可能失败。所以Int()/Uint()/Float64()返回一个值和一个错误。
要留意这种不一致!如果我们将配置中 redis 端口改成非法的数字 x6381,那么运行程序将报错:
2020/01/14 22:43:13 strconv.ParseInt: parsing "x6381": invalid syntax
Must*便捷方法
如果每次取值都需要进行错误判断,那么代码写起来会非常繁琐。为此,go-ini也提供对应的MustType(Type 为Init/Uint/Float64等)方法,这个方法只返回一个值。 同时它接受可变参数,如果类型无法转换,取参数中第一个值返回,并且该参数设置为这个配置的值,下次调用返回这个值:
package main import (
"fmt"
"log" "gopkg.in/ini.v1"
) func main() {
cfg, err := ini.Load("my.ini")
if err != nil {
log.Fatal("Fail to read file: ", err)
} redisPort, err := cfg.Section("redis").Key("port").Int()
if err != nil {
fmt.Println("before must, get redis port error:", err)
} else {
fmt.Println("before must, get redis port:", redisPort)
} fmt.Println("redis Port:", cfg.Section("redis").Key("port").MustInt(6381)) redisPort, err = cfg.Section("redis").Key("port").Int()
if err != nil {
fmt.Println("after must, get redis port error:", err)
} else {
fmt.Println("after must, get redis port:", redisPort)
}
}
配置文件还是 redis 端口为非数字 x6381 时的状态,运行程序:
before must, get redis port error: strconv.ParseInt: parsing "x6381": invalid syntax
redis Port: 6381
after must, get redis port: 6381
我们看到第一次调用Int返回错误,以 6381 为参数调用MustInt之后,再次调用Int,成功返回 6381。MustInt源码也比较简单:
// 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
}
分区操作
获取信息
在加载配置之后,可以通过Sections方法获取所有分区,SectionStrings()方法获取所有分区名。
sections := cfg.Sections()
names := cfg.SectionStrings() fmt.Println("sections: ", sections)
fmt.Println("names: ", names)
运行输出 3 个分区:
[DEFAULT mysql redis]
调用Section(name)获取名为name的分区,如果该分区不存在,则自动创建一个分区返回:
newSection := cfg.Section("new")
fmt.Println("new section: ", newSection)
fmt.Println("names: ", cfg.SectionStrings())
创建之后调用SectionStrings方法,新分区也会返回:
names: [DEFAULT mysql redis new]
也可以手动创建一个新分区,如果分区已存在,则返回错误:
err := cfg.NewSection("new")
父子分区
在配置文件中,可以使用占位符%(name)s表示用之前已定义的键name的值来替换,这里的s表示值为字符串类型:
NAME = ini
VERSION = v1
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s [package]
CLONE_URL = https://%(IMPORT_PATH)s [package.sub]
上面在默认分区中设置IMPORT_PATH的值时,使用了前面定义的NAME和VERSION。 在package分区中设置CLONE_URL的值时,使用了默认分区中定义的IMPORT_PATH。
我们还可以在分区名中使用.表示两个或多个分区之间的父子关系,例如package.sub的父分区为package,package的父分区为默认分区。 如果某个键在子分区中不存在,则会在它的父分区中再次查找,直到没有父分区为止:
cfg, err := ini.Load("parent_child.ini")
if err != nil {
fmt.Println("Fail to read file: ", err)
return
}
fmt.Println("Clone url from package.sub:", cfg.Section("package.sub").Key("CLONE_URL").String())
运行程序输出:
Clone url from package.sub: https://gopkg.in/ini.v1
子分区中package.sub中没有键CLONE_URL,返回了父分区package中的值。
保存配置
有时候,我们需要将生成的配置写到文件中。例如在写工具的时候。保存有两种类型的接口,一种直接保存到文件,另一种写入到io.Writer中:
err = cfg.SaveTo("my.ini")
err = cfg.SaveToIndent("my.ini", "\t")
cfg.WriteTo(writer)
cfg.WriteToIndent(writer, "\t")
下面我们通过程序生成前面使用的配置文件my.ini并保存:
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")
}
运行程序,生成两个文件my.ini和my-pretty.ini,同时控制台输出文件内容。
my.ini: app_name = awesome web
log_level = DEBUG [mysql]
ip = 127.0.0.1
port = 3306
user = root
password = 123456
database = awesome [redis]
ip = 127.0.0.1
port = 6381
my-pretty.ini:
app_name = awesome web
log_level = DEBUG [mysql]
ip = 127.0.0.1
port = 3306
user = root
password = 123456
database = awesome [redis]
ip = 127.0.0.1
port = 6381
*Indent方法会对子分区下的键增加缩进,看起来美观一点。
分区与结构体字段映射
定义结构变量,加载完配置文件后,调用MapTo将配置项赋值到结构变量的对应字段中。
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("my.ini")
if err != nil {
fmt.Println("load my.ini failed: ", err)
} c := Config{}
cfg.MapTo(&c) fmt.Println(c)
}
MapTo内部使用了反射,所以结构体字段必须都是导出的。如果键名与字段名不相同,那么需要在结构标签中指定对应的键名。 这一点与 Go 标准库encoding/json和encoding/xml不同。标准库json/xml解析时可以将键名app_name对应到字段名AppName。 或许这是go-ini库可以优化的点?
先加载,再映射有点繁琐,直接使用ini.MapTo将两步合并:
err = ini.MapTo(&c, "my.ini")
也可以只映射一个分区:
mysqlCfg := MySQLConfig{}
err = cfg.Section("mysql").MapTo(&mysqlCfg)
还可以通过结构体生成配置:
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
}
golang操作ini文件包之go-ini的更多相关文章
- 关于C#操作INI文件的总结
原文:关于C#操作INI文件的总结 INI文件其实是一种具有特定结构的文本文件,它的构成分为三部分,结构如下: [Section1]key 1 = value2key 1 = value2--[S ...
- WPF操作ini 文件的读写示例
/// <summary> /// IniFiles 的摘要说明. /// 示例文件路径:C:\file.ini /// [Server] //[*] 表示缓存区 /// name=loc ...
- Openstack Paste.ini 文件详解
目录 目录 pasteini 配置文件详解 composite pipeline filter app DEFAULT server Request 被 pasteini 处理的流程 如何加载 pas ...
- WIN32读写INI文件方法
在程序中经常要用到设置或者其他少量数据的存盘,以便程序在下一次执行的时候可以使用,比如说保存本次程序执行时窗口的位置.大小.一些用户设置的 数据等等,在 Dos 下编程的时候,我们一般自己产生一个 ...
- 封装 INI 文件读写函数
delphi读写ini文件实例 //--两个过程,主要实现:窗体关闭的时候,文件保存界面信息:窗体创建的时候,程序读取文件文件保存的信息. //--首先要uses IniFiles(单元) //--窗 ...
- Windows中读写ini文件
.ini 文件是Initialization File的缩写,即初始化文件,是windows的系统配置文件所采用的存储格式,来配置应用软件以实现不同用户的要求.配置文件有很多种如ini配置文件,XML ...
- C#读写.ini文件
转载来源: http://blog.csdn.net/source0573/article/details/49668079 https://www.cnblogs.com/wang726zq/arc ...
- C# 创建INI文件,写入并可读取。----转载
基于C#winform设计. 首先创建一个类,我命名为IniFiles.并引入命名空间using System.Runtime.InteropServices; 接着,声明API函数 [DllImpo ...
- ini 文件
INI文件是一个无固定标准格式的配置文件.它以简单的文字与简单的结构组成,常常使用在视窗操作系统,或是其他操作系统上,许多程序也会采用INI文件做为设置程序之用.视窗操作系统后来以注册表的形式替换掉I ...
- Lodop打印维护PRINT_SETUP本地缓存ini文件
针对千差万别的客户端,Lodop提供了打印维护(PRINT_SETUP),可以针对某个客户端微调,调整结果保存在客户端本地,不会影响其他访问网站的用户的使用. 打印维护使用方法:1.PRINT_INI ...
随机推荐
- lxml官方入门教程(The lxml.etree Tutorial)翻译
lxml官方入门教程(The lxml.etree Tutorial)翻译 说明: 首次发表日期:2024-09-05 官方教程链接: https://lxml.de/tutorial.html 使用 ...
- Kubelet证书自动续签(为 kubelet 配置证书轮换)
1.概述 Kubelet 使用证书进行 Kubernetes API 的认证. 默认情况下,这些证书的签发期限为一年,所以不需要太频繁地进行更新. Kubernetes 包含特性 Kubelet 证书 ...
- 如何在 Nuxt 3 中有效使用 TypeScript
title: 如何在 Nuxt 3 中有效使用 TypeScript date: 2024/9/9 updated: 2024/9/9 author: cmdragon excerpt: 摘要:本文详 ...
- 使用VSCode搭建UniApp + TS + Vue3 + Vite项目
uniapp是一个使用Vue.js开发所有前端应用的框架,开发者编写一套代码,可发布到iOS.Android.以及各种小程序.深受广大前端开发者的喜爱.uniapp官方也提供了自己的IDE工具HBui ...
- ToCom:一次训练随意使用,华为提出通用的ViT标记压缩器 | ECCV 2024
标记压缩通过减少冗余标记的数量(例如,修剪不重要的标记或合并相似的标记)来加快视觉变换器(ViTs)的训练和推理.然而,当这些方法应用于下游任务时,如果训练和推理阶段的压缩程度不匹配,会导致显著的性能 ...
- Atcoder Beginner Contest 367
A.Shout Everyday \(\text{Diff }43\) 给你 \(24\) 小时制下的 \(A,B,C\) 三个时刻,问 \(A\) 是否在 \([B,C]\) 范围内 考虑到先将 \ ...
- 如何关闭每次打开启动软件前的弹窗(用户账户控制)你要允许此应用.WIN11、10、7
1.先点击任务栏内的搜索,输入"控制面板",然后点开 2.然后在右上角输入"更改用户",然后在下方点击"更改用户账户控制设置" 3.然后把& ...
- base64 是什么,有什么作用?
base64 是图片编码的一种形式,可以替代图片的url进行网络访问和请求等操作: 使用图片的url形式操作图片,每次都要请求一次网络,因为每次请求都是一个http:都是一个网络开销,都是对服务器的负 ...
- 云原生周刊:Gateway API 1.0.0 发布 | 2023.11.6
开源项目推荐 Kueue Kueue 是一套用于作业队列的 API 和控制器.它是作业级管理器,可决定何时允许作业启动(如创建 pod),何时停止作业(如删除活动 pod). Reloader 一个 ...
- 【2022noip多校】异或
[题目描述] 对于一个元素介于 \([0,2^m)\) 且互不相同的长度为 \(n\) 的序列 \(a_1, a_2 ...,a_n\) ,定义它的特征序列为 \(p_0,p_1,...,p_{2^m ...