日常开发中读取配置文件包含以下几种格式:

  • json 格式字符串
  • K=V 键值对
  • xml 文件
  • yml 格式文件
  • toml 格式文件

前面两种书写简单,解析过程也比较简单。xml形式书写比较累赘,yml是树形结构,为简化配置而生,toml是一种有着自己语法规则的配置文件格式,我们一一来看使用方式,各位看官自行比较哪种更加实用。

1.读取json格式的文件

package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"sync"
) type Configs map[string]json.RawMessage
var configPath string = "c:/test.json" type MainConfig struct {
Port string `json:"port"`
Address string `json:"address"`
} var conf *MainConfig
var confs Configs var instanceOnce sync.Once //从配置文件中载入json字符串
func LoadConfig(path string) (Configs, *MainConfig) {
buf, err := ioutil.ReadFile(path)
if err != nil {
log.Panicln("load config conf failed: ", err)
}
mainConfig := &MainConfig{}
err = json.Unmarshal(buf, mainConfig)
if err != nil {
log.Panicln("decode config file failed:", string(buf), err)
}
allConfigs := make(Configs, 0)
err = json.Unmarshal(buf, &allConfigs)
if err != nil {
log.Panicln("decode config file failed:", string(buf), err)
} return allConfigs, mainConfig
} //初始化 可以运行多次
func SetConfig(path string) {
allConfigs, mainConfig := LoadConfig(path)
configPath = path
conf = mainConfig
confs = allConfigs
} // 初始化,只能运行一次
func Init(path string) *MainConfig {
if conf != nil && path != configPath {
log.Printf("the config is already initialized, oldPath=%s, path=%s", configPath, path)
}
instanceOnce.Do(func() {
allConfigs, mainConfig := LoadConfig(path)
configPath = path
conf = mainConfig
confs = allConfigs
}) return conf
} //初始化配置文件 为 struct 格式
func Instance() *MainConfig {
if conf == nil {
Init(configPath)
}
return conf
} //初始化配置文件 为 map格式
func AllConfig() Configs {
if conf == nil {
Init(configPath)
}
return confs
} //获取配置文件路径
func ConfigPath() string {
return configPath
} //根据key获取对应的值,如果值为struct,则继续反序列化
func (cfg Configs) GetConfig(key string, config interface{}) error {
c, ok := cfg[key]
if ok {
return json.Unmarshal(c, config)
} else {
return fmt.Errorf("fail to get cfg with key: %s", key)
}
} func main() {
path := ConfigPath()
fmt.Println("path: ",path)
Init(path)
value := confs["port"]
fmt.Println(string(value))
}

json格式文件内容:

{
"port": "7788",
"address": "47.95.34.2"
}

运行结果:

path:  c:/test.json
"7788"

2. 读取key=value类型的配置文件

package main

import (
"bufio"
"io"
"os"
"strings"
) //读取key=value类型的配置文件
func InitConfig(path string) map[string]string {
config := make(map[string]string) f, err := os.Open(path)
defer f.Close()
if err != nil {
panic(err)
} r := bufio.NewReader(f)
for {
b, _, err := r.ReadLine()
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
s := strings.TrimSpace(string(b))
index := strings.Index(s, "=")
if index < 0 {
continue
}
key := strings.TrimSpace(s[:index])
if len(key) == 0 {
continue
}
value := strings.TrimSpace(s[index+1:])
if len(value) == 0 {
continue
}
config[key] = value
}
return config
} func main() {
config := InitConfig("c:/1.txt")
ip := config["ip"]
port := config["port"] fmt.Println("ip=",string(ip)," port=",string(port))
}

配置文件类容:

ip=127.0.0.1
port=3344

运行结果:

ip=127.0.0.1  port=3344

3. 读取yml格式文件

Java中SpringBoot支持使用yml格式的配置文件作为替代properties文件的一种方式。跟properties文件相比,好处就是层级目录,相同的前缀都在该前缀下,前缀只用写一次即可。Go也支持yml文件解析,只是麻烦的程度真的是,,,不想写!

我们先定义一个yml文件:

port: 8080
ip: 127.0.0.1
host: www.baidu.com spring:
redis:
host: redis.dns.baidu.com
port: 6379
dataBase: 0
timeout: 2000

解析代码如下:

package utils

import (
"fmt"
"gopkg.in/yaml.v2"
"io/ioutil"
) //解析yml文件
type BaseInfo struct {
Port string `yaml:"port"`
Ip string `yaml:"ip"`
Host string `yaml:"host"`
Spring RedisEntity `yaml:"spring"`
} type RedisEntity struct {
Redis RedisData `yaml:"redis"`
} type RedisData struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
DataBase string `yaml:"dataBase"`
Timeout string `yaml:"timeout"`
} func (c *BaseInfo) GetConf() *BaseInfo {
yamlFile, err := ioutil.ReadFile("c:/1.yml")
if err != nil {
fmt.Println(err.Error())
}
err = yaml.Unmarshal(yamlFile, c)
if err != nil {
fmt.Println(err.Error())
}
return c
}

解释一下:以上yml文件中是有三层目录结构,所以需要定义三个struct,每个struct分别包含每一层的字段。这样说你应该就能明白如何解析。

运行一下test方法来验证上面程序:

package main

import (
"fmt"
"goProject/src/utils"
"testing"
) func Test(t *testing.T) {
info := utils.BaseInfo{}
conf := info.GetConf()
fmt.Println(conf.Host)
}

可以看到将yml中的树形结构解析为BaseInfo对象。

4. 读取toml格式文件

仿佛我感觉这个比读取yml文件更为痛苦,因为在写toml文件的时候,还有一定的语法规则,仿佛在告诉你别停,继续学习。

TOML 的全称是Tom’s Obvious, Minimal Language,因为它的作者是 GitHub联合创始人Tom Preston-Werner 。 TOML 的目标是成为一个极简的配置文件格式。TOML 被设计成可以无歧义地被映射为哈希表的结构,从而可以被多种语言解析。

举个例子:

port=8080    

[user]
name="xiaoming"
age=14
sex=1 [database]
servers=["127.0.0.1","127.0.0.2","127.0.0.3"]
connection_max=5000
enabled=true [servers]
# 你可以依照你的意愿缩进。使用空格或Tab。TOML不会在意。
[servers.a]
ip="34.23.1.4"
port=6379 [servers.b]
ip="34.23.1.6"
port=9921 #嵌套
[nest]
data=[["n1","n2"],[1,2]] # 在数组里换行没有关系。
names = [
"li",
"wang"
]

下面来解释一下toml格式的书写规范。

首先:TOML 是大小写敏感的。

常见的语法规则:

  1. 注释

    使用 # 表示注释。

  2. 字符串

字符串以""包裹,里面的字符必须是 UTF-8 格式。引号、反斜杠和控制字符(U+0000 到 U+001F)需要转义。

常用的转义序列:

\b     - backspace       (U+0008)
\t - tab (U+0009)
\n - linefeed (U+000A)
\f - form feed (U+000C)
\r - carriage return (U+000D)
\" - quote (U+0022)
\/ - slash (U+002F)
\\ - backslash (U+005C)
\uXXXX - unicode (U+XXXX)
  1. 布尔值

    true

    false

  2. 日期

    使用ISO8601格式日期:

    2019-05-03T22:44:26Z
  3. 数组

    数组使用方括号包裹。空格会被忽略。元素使用逗号分隔。注意,不允许混用数据类型。

    [ 1, 2, 3 ]
    [ "red", "yellow", "green" ]
    [ [ 1, 2 ], [3, 4, 5] ]
    [ [ 1, 2 ], ["a", "b", "c"] ] # 这是可以的。
    [ 1, 2.0 ] # 注意:这是不行的。
  4. 数值类型

    数值类型严格区分整数和浮点数。这两个不是一种类型。

  5. 字典对象

    多个kv集合在一起组成字典对象。字典对象的名字用[]包含起来,单独作为一行。

    解释一下这种格式对应的json格式:

    [servers.a]
    ip="34.23.1.4"
    port=6379 { "servers": { "a": { "ip": "34.23.1.4","port":6379}}}

    我们写一个小程序来解析上面的toml文件:

    package utils
    
    import (
    "fmt"
    "github.com/BurntSushi/toml"
    "io/ioutil"
    "os"
    ) type BaseData struct {
    Db DataBase `toml:"dataBase"`
    Se Servers `toml:"servers"`
    } type DataBase struct {
    Servers []string `toml:"servers"`
    ConnectionMax int `toml:"connection_max"`
    Enabled bool `toml:"enabled"`
    } type Servers struct {
    A ServerEn `toml:"a"`
    B ServerEn `toml:"b"`
    } type ServerEn struct {
    IP string `toml:"ip"`
    Port int `toml:"port"`
    } func ReadConf(fname string) (p *BaseData, err error) {
    var (
    fp *os.File
    fcontent []byte
    )
    p = new(BaseData)
    if fp, err = os.Open(fname); err != nil {
    fmt.Println("open error ", err)
    return
    } if fcontent, err = ioutil.ReadAll(fp); err != nil {
    fmt.Println("ReadAll error ", err)
    return
    } if err = toml.Unmarshal(fcontent, p); err != nil {
    fmt.Println("toml.Unmarshal error ", err)
    return
    }
    return
    }

    注意看我定义的对象,也是逐层解析。

    测试类:

    package main
    
    import (
    "fmt"
    "goProject/src/utils"
    "testing"
    ) func Test(t *testing.T) {
    p, _ := utils.ReadConf("c:/1.toml")
    fmt.Println(p)
    }

Go中配置文件读取的几种方式的更多相关文章

  1. linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...

  2. JavaScript学习12 JS中定义对象的几种方式

    JavaScript学习12 JS中定义对象的几种方式 JavaScript中没有类的概念,只有对象. 在JavaScript中定义对象可以采用以下几种方式: 1.基于已有对象扩充其属性和方法 2.工 ...

  3. Shell脚本中执行mysql的几种方式(转)

    Shell脚本中执行mysql的几种方式(转) 对于自动化运维,诸如备份恢复之类的,DBA经常需要将SQL语句封装到shell脚本.本文描述了在Linux环境下mysql数据库中,shell脚本下调用 ...

  4. Java中HashMap遍历的两种方式

    Java中HashMap遍历的两种方式 转]Java中HashMap遍历的两种方式原文地址: http://www.javaweb.cc/language/java/032291.shtml 第一种: ...

  5. JAVA中集合输出的四种方式

    在JAVA中Collection输出有四种方式,分别如下: 一) Iterator输出. 该方式适用于Collection的所有子类. public class Hello { public stat ...

  6. android中解析文件的三种方式

    android中解析文件的三种方式     好久没有动手写点东西了,最近在研究android的相关技术,现在就android中解析文件的三种方式做以下总结.其主要有:SAX(Simple API fo ...

  7. python中逐行读取文件的最佳方式_Drupal_新浪博客

    python中逐行读取文件的最佳方式_Drupal_新浪博客 python中逐行读取文件的最佳方式    (2010-08-18 15:59:28)    转载▼    标签:    python   ...

  8. jQuery中开发插件的两种方式

    jQuery中开发插件的两种方式(附Demo) 做web开发的基本上都会用到jQuery,jQuery插件开发两种方式:一种是类扩展的方式开发插件,jQuery添加新的全局函数(jQuery的全局函数 ...

  9. Struts中的数据处理的三种方式

    Struts中的数据处理的三种方式: public class DataAction extends ActionSupport{ @Override public String execute() ...

随机推荐

  1. python初识(2)

    1 字符串格式占位符 1.1%s %d %% 占位符 预留 字符串 整型 (转义) name = input('name:') print ('你的名字是:%s'%(name)) 1.2 f" ...

  2. ajax请求中 两种csrftoken的发送方法

    通过ajax的方式发送两个数据进行加法运算 html页面 <body> <h3>index页面 </h3> <input type="text&qu ...

  3. .Net Core 创建和使用中间件

    1. 定义中间内容 1.1 必须有一个RequestDelegate 委托用了进入一个中间件 1.2 通过构造函数设置这个RequestDelegate委托 1.3 必须有一个方法Task Invok ...

  4. Java 新特性总结——简单实用

    lambda表达式 简介 lambda 表达式的语法 变量作用域 函数式接口 内置函数式接口 默认方法 Stream(流) 创建 stream Filter(过滤) Sorted(排序) Map(映射 ...

  5. TCP传输协议中如何解决丢包问题

    TCP在不可靠的网络上实现可靠的传输,必然会有丢包.TCP是一个“流”协议,一个详细的包将会被TCP拆分为好几个包上传,也是将会把小的封裝成大的上传,这就是说TCP粘包和拆包难题. 但是许多人有不同的 ...

  6. WinForm控件之【ListBox】

    基本介绍 列表控件,将一个或多个数据项列表展示供选择处理. 常设置属性 DataSource:绑定加载项的数据源,设置属性DisplayMember绑定需要显示字段名: ColumnWidth:当属性 ...

  7. jenkins +Jmeter 完成分布式性能测试

    1.Jmeter 压测机器配置. 下载Jmeter 版本:https://jmeter.apache.org/download_jmeter.cgi   我下的是5.1.1 将下载后的版本进行解压. ...

  8. C#3.0新增功能09 LINQ 基础05 使用 LINQ 进行数据转换

    连载目录    [已更新最新开发文章,点击查看详细] 语言集成查询 (LINQ) 不只是检索数据. 它也是用于转换数据的强大工具. 通过使用 LINQ查询,可以使用源序列作为输入,并通过多种方式对其进 ...

  9. TensorFlow(1)-基础知识点总结

    1. tensorflow简介 Tensorflow 是 google 开源的机器学习工具,在2015年11月其实现正式开源,开源协议Apache 2.0. Tensorflow采用数据流图(data ...

  10. [译]为任意网格计算tangent空间的基向量

    +BIT祝威+悄悄在此留下版了个权的信息说: [译]为任意网格计算tangent空间的基向量 Computing Tangent Space Basis Vectors for an Arbitrar ...