iGO實現之路 —— Config

chain發表於2018-01-22

本文為轉載,原文:iGO實現之路 —— Config

Golang

介紹

相信大多數的程式設計師在使用各種框架開發的時候,都會有一個配置檔案讓我們做一些配置,比如說資料庫的配置,快取的配置,除錯的配置等等,這些配置只要我們填寫之後就會生效。

今天,我就用go語言實現是個讀取配置檔案的小模組。可能並不是非常的完成,或者還會存在一些不安全的因素。這裡我將我的實現寫出來,望發現問題的同學們提出來,我們大家共同進步。

原始碼地址

igo-github原始碼地址

實現

config 模組的檔案列表如下圖所示: 檔案列表

  • conf 目錄為功能模組

  • example 目錄為測試程式碼

功能實現

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語言庫

相關文章