2016-10-04 67 views
2

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) 

} 
+0

如果它做的工作,我不会担心它。 Go不是关于优雅的代码,而是关于工作代码...... – RickyA

+1

也就是说,在访问它之前('tmp [0]'),我会放一个测试'如果len(tmp)> = 2',因为那可能会失败。 – RickyA

+0

@RickyA非常好的一点,我其实没有真的把任何错误检查在那里。将它添加到我的工作中,谢谢。 – Mikey

回答

4

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

接下来,我真的会考虑将在另一种格式的配置。因为你提出的不是一个标准。它接近Java's property file format (.properties),但即使属性文件可能包含Unicode序列,因此您的代码不是有效的格式解析器,因为它根本不处理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分析器。请参阅执行github.com/go-yaml/yaml

如果您不想更改格式,那么我只会使用您发布的代码,因为它完全按照您希望的方式执行:逐行处理输入,并从一对name = value解析对每一行。 它以一种明确而明显的方式做到这一点。使用CSV或任何其他阅读器为此目的是不好的,因为它们隐藏了底层的内容(他们故意并正确地隐藏了格式特定的细节和转换)。一个CSV阅读器一个CSV阅读器第一个;即使您更改了制表符/逗号符号:它将解释某些转义序列,并可能会给您不同于您在纯文本编辑器中看到的数据。从您的角度来看,这是一种意想不到的行为,但嘿,您的输入是CSV格式的而不是,但您要求读者将其解释为 CSV!

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

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) 

输出是一样的。试试Go Playground

使用bufio.Scanner还有另一个优点:bufio.NewScanner()接受io.Reader,即“所有东西都是字节源”的通用接口。这意味着如果您的配置存储在一个文件中,您甚至不需要将所有配置读入内存,只需打开该文件即可与os.Open()返回值*os.File也实现io.Reader,因此您可以直接将*os.File值传递到bufio.NewScanner()(并且因此bufio.Scanner将从文件中读取,而不是像上面的示例中那样从内存缓冲区中读取)。

+1

真的不能和那个明智的答案争辩。主要是我选择纯文本格式的唯一原因是因为我看到它在其他语言中处理。在完全消化你的答案之后,将提出你提到的另一个问题。谢谢,但:D – Mikey

+0

@Pigeon做了几个更多的编辑,使其更有用/信息。 – icza

3

1 - 您可以使用csv.NewReader只是一个函数调用r.ReadAll()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] 
    } 
} 

ou tput的:

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

这是一个有趣的方法,如果我想用'strings.TrimSpace()'修剪任何空格,它是循环元素并更新它们的情况,还是有办法将它应用到csv读者?只是所以我可以避免'[Var2 Value2]' – Mikey

+0

看起来很有趣,会看看其他人是否有任何建议。如果没有,我稍后会接受这个答案。 – Mikey

相关问题