ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Go中配置文件读取的几种方式

2021-10-11 16:32:50  阅读:197  来源: 互联网

标签:读取 err fmt toml Go path port string 配置文件


Go中配置文件读取的几种方式

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

  • 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)
    }
    

标签:读取,err,fmt,toml,Go,path,port,string,配置文件
来源: https://blog.csdn.net/qq_43520820/article/details/120706091

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有