GolangGolang程序员

iGO实现之路 —— Config

2018-01-22  本文已影响108人  ChainZhang

本文为转载,原文:iGO实现之路 —— Config

Golang

介绍

相信大多数的程序员在使用各种框架开发的时候,都会有一个配置文件让我们做一些配置,比如说数据库的配置,缓存的配置,调试的配置等等,这些配置只要我们填写之后就会生效。

今天,我就用go语言实现是个读取配置文件的小模块。可能并不是非常的完成,或者还会存在一些不安全的因素。这里我将我的实现写出来,望发现问题的同学们提出来,我们大家共同进步。

源码地址

igo-github源码地址

实现

config 模块的文件列表如下图所示:


文件列表

功能实现

config.go

由于我们的配置文件类型可能是多种多样的,比如ini,json等。所以,我首先得做一个Config的接口:

type Config interface{
    GetString(string) string
    GetInt(string)(int, error)
    GetInt64(string)(int64, error)
    GetFloat(string)(float64, error)
    GetBool(string)(bool, error)
}

从代码中看,我们的接口比较简单,就是一些从配置文件中读取各种类型的值,这里先暂时支持string,int,int64,float,bool类型的数据读取,后续如有需要再更新。

有了接口,就便于我们同意操作了。下面我们再做一个实例化的函数,这个就有点类型简单工厂模式了。根据参数创建不同类型的实例:

func NewConfig(adapter, filename string) (Config, error){
    path, err := GetCurrentPath(filename)
    if err != nil{
        return nil, err
    }
    switch adapter{
    case "ini":
        return GetIniConfig(path)
    default:
        return nil, errors.New("系统暂未处理该类型的配置文件")
    }
}

该函数有2个参数,adapter为我们适配的配置文件类型,如ini, json等。filename为我们的配置文件的相对路径。
代码通过不同的adapter返回不同的实例。该函数中调用了个GetCurrentPath函数。

下面,我们看下GetCurrentPath函数的实现:

func GetCurrentPath(filename string) (path string, err error ){
    path, err = filepath.Abs(filename)
    if err != nil {
        return
    }
    path = strings.Replace(path, "\\", "/", -1)
    path = strings.Replace(path, "\\\\", "/", -1)
    return
}

该函数的作用是将配置文件的相对路径转换为绝对路径。
至此,我们的公用的代码已经完成了。接下来就是各种不同的配置文件的实现了,也就是各种对于Config接口实现的方案。

完整的config.go代码如下:

package conf

import (
    "strings"
    "errors"

    "path/filepath"
)

type Config interface{
    GetString(string) string
    GetInt(string)(int, error)
    GetInt64(string)(int64, error)
    GetFloat(string)(float64, error)
    GetBool(string)(bool, error)
}

func NewConfig(adapter, filename string) (Config, error){
    path, err := GetCurrentPath(filename)
    if err != nil{
        return nil, err
    }
    switch adapter{
    case "ini":
        return GetIniConfig(path)
    default:
        return nil, errors.New("系统暂未处理该类型的配置文件")
    }
}

func GetCurrentPath(filename string) (path string, err error ){
    path, err = filepath.Abs(filename)
    if err != nil {
        return
    }
    path = strings.Replace(path, "\\", "/", -1)
    path = strings.Replace(path, "\\\\", "/", -1)
    return
}

ini

ini_config.go

INI文件格式是某些平台或软件上的配置文件的非正式标准,以节(section)和键(key)构成,常用于微软Windows操作系统中。这种配置文件的文件扩展名多为INI,故名。

INI是英文“初始化”(initialization)的缩写。正如该术语所表示的,INI文件被用来对操作系统或特定程序初始化或进行参数设置。

既然知道了ini文件,那么我们就看看怎么去读取其配置吧。
先看下对于IniConfig的结构体定义:

type IniConfig struct{
    ConfigMap  map[string]string
    strcet string
}

从config.go代码文件中的NewConfig函数中,我们看到了这样一个函数GetIniConfig,而该函数就是对于ini文件配置的初始化:

func GetIniConfig(filename string)(*IniConfig, error){
    middle := "."
    config :=  new(IniConfig)
    config.ConfigMap = make(map[string]string)
    //打开文件
    file, err := os.Open(filename)
    if err != nil{
        return nil, err
    }
    defer file.Close()
    read := bufio.NewReader(file)
    for{
        b, _, err := read.ReadLine()
        if err != nil {
            if err == io.EOF{
                break
            }
            return nil, err
        }
        str := strings.TrimSpace(string(b))
        //配置文件中的注释
        if strings.Index(str, "#") == 0{
            continue
        }
        //配置文件中的前缀处理
        n1 := strings.Index(str, "[")
        n2 := strings.LastIndex(str, "]")
        if n1 > -1 && n2 > -1 && n2 > n1 + 1{
            config.strcet = strings.TrimSpace(str[n1 + 1 : n2])
            continue
        }
        if len(config.strcet) < 1{
            continue
        }
        //
        eqIndex := strings.Index(str, "=")
        if eqIndex < 0{
            continue
        }
        eqLeft := strings.TrimSpace(str[0:eqIndex])
        if len(eqLeft) < 1{
            continue
        }
        eqRight := strings.TrimSpace(str[eqIndex+1:])
        pos := strings.Index(eqRight,"\t#")
        val := eqRight
        if pos > -1{
            val = strings.TrimSpace(eqRight[0:pos])
        }

        pos = strings.Index(eqRight, " #")
        if pos > -1{
            val = strings.TrimSpace(eqRight[0:pos])
        }

        pos = strings.Index(eqRight, "\t//")
        if pos > -1{
            val = strings.TrimSpace(eqRight[0:pos])
        }

        pos = strings.Index(eqRight, " //")
        if pos > -1{
            val = strings.TrimSpace(eqRight[0:pos])
        }
        if len(val) < 1{
            continue
        }
        key := config.strcet + middle + eqLeft
        config.ConfigMap[key] = strings.TrimSpace(val)
    }
    return config, nil
}

然后再有一个按key来获取配置的val的字符串的函数:

func (self *IniConfig) Get(key string)string{
    v, ok := self.ConfigMap[key]
    if ok{
        return v
    }
    return ""
}

剩下来就是对于Config接口的实现了:

func (self *IniConfig) GetString(key string)string{
    return self.Get(key)
}

func (self *IniConfig) GetInt(key string)(int, error){
    return strconv.Atoi(self.Get(key))
}

func (self *IniConfig) GetInt64(key string)(int64, error){
    return strconv.ParseInt(self.Get(key), 10, 64)
}

func (self *IniConfig) GetFloat(key string)(float64, error){
    return strconv.ParseFloat(self.Get(key), 64)
}

func (self *IniConfig) GetBool(key string)(bool, error){
    return strconv.ParseBool(self.Get(key))
}

ini_config.go的实现就这些。当然了,package和import也是肯定有的啦:

package conf

import (
    "strings"
    "strconv"

    "io"
    "bufio"
    "os"
)

上面的这些代码整合到一起就是ini_config.go的完整代码了。

测试

上面那些,也就简单的实现了配置文件的读取功能了。但是能不能用呢?我们简单的做个测试吧。
/example/config/app.config中写一些简单的配置信息:

[default]
string = string
int = 10
int64 = 100000
float = 123.456
bool = true

然后在/example/test_config.go读取试一下:

package main

import(
    "fmt"
    "igo/conf"
)

func main(){
    config,err := conf.NewConfig("ini", "config/app.config")
    if err != nil{
        fmt.Println(err.Error())
        return
    }
    strVal := config.GetString("default.string")
    fmt.Println("string value:", strVal)

    intVal, err := config.GetInt("default.int")
    if err != nil{
        fmt.Println("get int value error: ", err.Error())
    }
    fmt.Println("int value: ", intVal)

    int64Val, err := config.GetInt64("default.int64")
    if err != nil{
        fmt.Println("get int64 value error: ", err.Error())
    }
    fmt.Println("int64 value: ", int64Val)
    
    floatVal, err := config.GetFloat("default.float")
    if err != nil{
        fmt.Println("get float value error: ", err.Error())
    }
    fmt.Println("float value: ", floatVal)

    boolVal, err := config.GetBool("default.bool")
    if err != nil{
        fmt.Println("get bool value error: ", err.Error())
    }
    fmt.Println("bool value: ", boolVal)
}

结果如下:


config测试结果

转载请注明出处:
iGO实现之路 —— Config

iGO我的go语言库

上一篇下一篇

猜你喜欢

热点阅读