Go的新功能,基本上在我写的实际代码中,我打算从包含环境变量的文件中读取,即API_KEY=XYZ
。意味着我可以让它们脱离版本控制。以下解决方案“有效”但我觉得可能有更好的方法。
最终目标是能够像这样访问文件中的元素
m["API_KEY"]
这将打印XYZ
。这甚至可能已经存在,我正在重新发明轮子,我看到Go有环境变量,但它似乎不是我特意后的。
所以任何帮助都表示赞赏。
代码:
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)
}
答案 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]