• 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)需要转义。

    常用的转义序列:

         - backspace       (U+0008)
    	     - tab             (U+0009)
    
         - linefeed        (U+000A)
    f     - form feed       (U+000C)
    
         - 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)
      }
      
      
  • 相关阅读:
    6_java_maven
    线性代数 linear algebra
    hadoop_wordcount_1027
    hadoop_worddistinct_1030
    hadoop_wordcount_1023
    搭建伪分布式_笔记
    linux-sunrpc
    linux-volatile
    linux---asmlinkage
    dqs_linux-1
  • 原文地址:https://www.cnblogs.com/rickiyang/p/11074169.html
Copyright © 2020-2023  润新知