有没有更好的方法来解析这张地图?

时间:2016-10-04 08:01:21

标签: dictionary go config

Go的新功能,基本上在我写的实际代码中,我打算从包含环境变量的文件中读取,即API_KEY=XYZ。意味着我可以让它们脱离版本控制。以下解决方案“有效”但我觉得可能有更好的方法。

最终目标是能够像这样访问文件中的元素 m["API_KEY"]这将打印XYZ。这甚至可能已经存在,我正在重新发明轮子,我看到Go有环境变量,但它似乎不是我特意后的。

所以任何帮助都表示赞赏。

Playground

代码:

package main

import (
    "fmt"
    "strings"
)

var m = make(map[string]string)

func main() {

    text := `Var1=Value1
    Var2=Value2
    Var3=Value3`

    arr := strings.Split(text, "\n")

    for _, value := range arr {
        tmp := strings.Split(value, "=")
        m[strings.TrimSpace(tmp[0])] = strings.TrimSpace(tmp[1])
    }

    fmt.Println(m)

}

2 个答案:

答案 0 :(得分:4)

首先,我建议您阅读此相关问题:How to handle configuration in Go

接下来,我会考虑以其他格式存储您的配置。因为你的建议不是标准。它接近Java's property file format (.properties),但即使属性文件也可能包含Unicode序列,因此您的代码不是有效的.properties格式解析器,因为它根本不处理Unicode序列。

相反,我建议使用JSON,因此您可以使用Go或任何其他语言轻松解析它,并且有许多工具可以编辑JSON文本,但它仍然是人性化的。

使用JSON格式,将其解码为map只是一个函数调用:json.Unmarshal()。它看起来像这样:

text := `{"Var1":"Value1", "Var2":"Value2", "Var3":"Value3"}`

var m map[string]string
if err := json.Unmarshal([]byte(text), &m); err != nil {
    fmt.Println("Invalid config file:", err)
    return
}

fmt.Println(m)

输出(在Go Playground上尝试):

map[Var1:Value1 Var2:Value2 Var3:Value3]

json包将为您处理格式化和转义,因此您不必担心其中任何一个。它还会为您检测并报告错误。此外,JSON更灵活,您的配置可能包含数字,文本,数组等。所有这些都是“免费”的,因为您选择了JSON格式。

另一种流行的配置格式是YAML,但Go标准库不包含YAML解析器。请参阅Go实施github.com/go-yaml/yaml

如果您不想更改格式,那么我只会使用您发布的代码,因为它完全符合您的要求:逐行处理输入,并解析{{1}从每一行配对。 它以明确而明显的方式实现。为此目的使用CSV或任何其他阅读器是不好的,因为它们隐藏了引擎盖下的内容(它们有意且正确地隐藏格式特定的细节和转换)。 CSV阅读器首先是 CSV阅读器;即使您更改制表符/逗号符号:它解释某些转义序列,并且可能会提供与您在纯文本编辑器中看到的数据不同的数据。从您的角度来看,这是一种意想不到的行为,但是,您的输入是而不是的CSV格式,但您要求读者将其解释为 CSV!

我将为您的解决方案添加的一项改进是使用bufio.Scanner。它可以用于逐行读取输入,并处理不同样式的换行序列。它看起来像这样:

name = value

输出是一样的。在Go Playground上尝试。

使用text := `Var1=Value1 Var2=Value2 Var3=Value3` scanner := bufio.NewScanner(strings.NewReader(text)) m := map[string]string{} for scanner.Scan() { parts := strings.Split(scanner.Text(), "=") if len(parts) == 2 { m[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) } } if err := scanner.Err(); err != nil { fmt.Println("Error encountered:", err) } fmt.Println(m) 还有另一个好处:bufio.NewScanner()接受io.Reader,即“所有事物都是字节源”的通用接口。这意味着如果您的配置存储在一个文件中,您甚至不必将所有配置读入内存,您只需打开该文件,例如使用os.Open()返回*os.File的值,该值也会实现bufio.Scanner,因此您可以直接将io.Reader值传递给*os.File(以及{{1}将从文件读取,而不是像上例中那样从内存缓冲区读取。

答案 1 :(得分:3)

1-您可以使用r.ReadAll()csv.NewReader的一个函数调用encoding/csv阅读所有内容,其中包含:

r.Comma = '='
r.TrimLeadingSpace = true

结果为[][]string输入订单已保留,请在The Go Playground上试用:

package main

import (
    "encoding/csv"
    "fmt"
    "strings"
)

func main() {
    text := `Var1=Value1
    Var2=Value2
    Var3=Value3`

    r := csv.NewReader(strings.NewReader(text))
    r.Comma = '='
    r.TrimLeadingSpace = true

    all, err := r.ReadAll()
    if err != nil {
        panic(err)
    }
    fmt.Println(all)
}

输出:

[[Var1 Value1] [Var2 Value2] [Var3 Value3]]

2-您可以微调csv.ReadAll()以将输出转换为地图,但订单不会保留,请在The Go Playground上尝试:

package main

import (
    "encoding/csv"
    "fmt"
    "io"
    "strings"
)

func main() {
    text := `Var1=Value1
    Var2=Value2
    Var3=Value3`
    r := csv.NewReader(strings.NewReader(text))
    r.Comma = '='
    r.TrimLeadingSpace = true
    all, err := ReadAll(r)
    if err != nil {
        panic(err)
    }
    fmt.Println(all)
}

func ReadAll(r *csv.Reader) (map[string]string, error) {
    m := make(map[string]string)
    for {
        tmp, err := r.Read()
        if err == io.EOF {
            return m, nil
        }
        if err != nil {
            return nil, err
        }
        m[tmp[0]] = tmp[1]
    }
}

输出:

map[Var2:Value2 Var3:Value3 Var1:Value1]