duanlao6573 2016-10-04 08:01
浏览 78
已采纳

有没有更好的方法来解析此Map?

Fairly new to Go, essentially in the actual code I'm writing I plan to read from a file which will contain environment variables, i.e. API_KEY=XYZ. Means I can keep them out of Version control. The below solution 'works' but I feel like there is probably a better way of doing it.

The end goal is to be able to access the elements from the file like so m["API_KEY"] and that would print XYZ. This may even already exist and I'm re-inventing the wheel, I saw Go has environment variables but it didn't seem to be what I was after specifically.

So any help is appreciated.

Playground

Code:

package main

import (
    "fmt"
    "strings"
)

var m = make(map[string]string)

func main() {

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

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

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

    fmt.Println(m)

}
  • 写回答

2条回答 默认 最新

  • douyan1944 2016-10-04 10:22
    关注

    First, I would recommend to read this related question: How to handle configuration in Go

    Next, I would really consider storing your configuration in another format. Because what you propose isn't a standard. It's close to Java's property file format (.properties), but even property files may contain Unicode sequences and thus your code is not a valid .properties format parser as it doesn't handle Unicode sequences at all.

    Instead I would recommend to use JSON, so you can easily parse it with Go or with any other language, and there are many tools to edit JSON texts, and still it is human-friendly.

    Going with the JSON format, decoding it into a map is just one function call: json.Unmarshal(). It could look like this:

    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)
    

    Output (try it on the Go Playground):

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

    The json package will handle formatting and escaping for you, so you don't have to worry about any of those. It will also detect and report errors for you. Also JSON is more flexible, your config may contain numbers, texts, arrays, etc. All those come for "free" just because you chose the JSON format.

    Another popular format for configuration is YAML, but the Go standard library does not include a YAML parser. See Go implementation github.com/go-yaml/yaml.

    If you don't want to change your format, then I would just use the code you posted, because it does exactly what you want it to do: process input line-by-line, and parse a name = value pair from each line. And it does it in a clear and obvious way. Using a CSV or any other reader for this purpose is bad because they hide what's under the hood (they intentionally and rightfully hide format specific details and transformations). A CSV reader is a CSV reader first; even if you change the tabulator / comma symbol: it will interpret certain escape sequences and might give you different data than what you see in a plain text editor. This is an unintended behavior from your point of view, but hey, your input is not in CSV format and yet you asked a reader to interpret it as CSV!

    One improvement I would add to your solution is the use of bufio.Scanner. It can be used to read an input line-by-line, and it handles different styles of newline sequences. It could look like this:

    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)
    

    Output is the same. Try it on the Go Playground.

    Using bufio.Scanner has another advantage: bufio.NewScanner() accepts an io.Reader, the general interface for "all things being a source of bytes". This means if your config is stored in a file, you don't even have to read all the config into the memory, you can just open the file e.g. with os.Open() which returns a value of *os.File which also implements io.Reader, so you may directly pass the *os.File value to bufio.NewScanner() (and so the bufio.Scanner will read from the file and not from an in-memory buffer like in the example above).

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 测距传感器数据手册i2c
  • ¥15 RPA正常跑,cmd输入cookies跑不出来
  • ¥15 求帮我调试一下freefem代码
  • ¥15 matlab代码解决,怎么运行
  • ¥15 R语言Rstudio突然无法启动
  • ¥15 关于#matlab#的问题:提取2个图像的变量作为另外一个图像像元的移动量,计算新的位置创建新的图像并提取第二个图像的变量到新的图像
  • ¥15 改算法,照着压缩包里边,参考其他代码封装的格式 写到main函数里
  • ¥15 用windows做服务的同志有吗
  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法