V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
wewin
V2EX  ›  Go 编程语言

请教一个包导入问题,百思不得解

  •  
  •   wewin · 2019-06-19 08:37:17 +08:00 · 2887 次点击
    这是一个创建于 1773 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目结构如下:

    decoder
          ├── config.json
          └── decoder.go
    

    config.json 文件内容

    {
        "ServerInfo": {
            "Host": "127.0.0.1:8888"
        },
        "RedisInfo": {
            "Host": "127.0.0.1:6379",
            "MaxIdle":     16,
            "MaxActive":   0,
            "IdleTimeout": 300
        }
    }
    

    decoder.go 文件内容

    package main
    
    import (
        "encoding/json"
        "fmt"
        "os"
        "time"
    )
    
    type Config struct{}
    
    type ConfigurationType struct {
        ServerInfo ServerInfoType
        RedisInfo  RedisInfoType
    }
    
    type ServerInfoType struct {
        Host string
    }
    
    type RedisInfoType struct {
        Host        string
        MaxIdle     int
        MaxActive   int
        IdleTimeout time.Duration
    }
    
    var Configuration = ConfigurationType{}
    
    func (this Config) InitConfig() {
        file, _ := os.Open("config.json")
        defer file.Close()
        decoder := json.NewDecoder(file)
        Configuration = ConfigurationType{}
        err := decoder.Decode(&Configuration)
        if err != nil {
            fmt.Println("Error: ", err)
        }
        fmt.Printf("Configuration: %v\n", Configuration)
    }
    
    func main() {
        conf := Config{}
        conf.InitConfig()
    }
    
    

    这种情况下我运行:go run decoder.go , 输出结果如下:

    Configuration: {{127.0.0.1:8888} {127.0.0.1:6379 16 0 300ns}}
    

    但是我把这个 main 包改为 decoder 包,项目结构调整如下

    .
    ├── decoder
    │   ├── config.json
    │   └── decoder.go
    └── main.go
    

    config.json 文件内容

    
    {
        "ServerInfo": {
            "Host": "127.0.0.1:8888"
        },
        "RedisInfo": {
            "Host": "127.0.0.1:6379",
            "MaxIdle":     16,
            "MaxActive":   0,
            "IdleTimeout": 300
        }
    }
    

    decoder.go 文件内容

    package decoder
    
    import (
        "encoding/json"
        "fmt"
        "os"
        "time"
    )
    
    type Config struct{}
    
    type ConfigurationType struct {
        ServerInfo ServerInfoType
        RedisInfo  RedisInfoType
    }
    
    type ServerInfoType struct {
        Host string
    }
    
    type RedisInfoType struct {
        Host        string
        MaxIdle     int
        MaxActive   int
        IdleTimeout time.Duration
    }
    
    var Configuration = ConfigurationType{}
    
    func (this Config) InitConfig() {
        file, _ := os.Open("config.json")
        defer file.Close()
        decoder := json.NewDecoder(file)
        Configuration = ConfigurationType{}
        err := decoder.Decode(&Configuration)
        if err != nil {
            fmt.Println("Error: ", err)
        }
        fmt.Printf("Configuration: %v\n", Configuration)
    }
    

    main.go 文件内容

    package main
    
    import "initTest/decoder"
    
    func main() {
        t := decoder.Config{}
        t.InitConfig()
    }
    

    测试运行 go run main.go 报错如下:

    Error:  invalid argument
    Configuration: {{} { 0 0 0s}}
    

    请问是否有人知道是什么原因?

    11 条回复    2019-06-24 08:51:40 +08:00
    freestyle
        1
    freestyle  
       2019-06-19 08:44:33 +08:00 via iPhone   ❤️ 1
    这不是包导入的问题, file, _ := os.Open("config.json") 你用的是相对路径,会从运行的工作目录(执行 go run main.go 的目录)下查找,实际上代码运行到这一步就出错了,找不到文件. 配置文件路径应该用函数参数去传递,而不是写死在函数里面. 要正确处理 error.
    wewin
        2
    wewin  
    OP
       2019-06-19 08:52:46 +08:00
    @freestyle 哎哟,我去,被闪了腰。现在用的语言文件找不到直接是跑出了异常,go 语言这种直接返回 err 的还真的不太习惯。

    将 decoder.go InitConfig 方法改了:
    ```
    ...
    func (this Config) InitConfig() {
    file, err := os.Open("config.json")
    defer file.Close()
    if err != nil {
    fmt.Printf("Open file error: %v\n", err)
    }

    decoder := json.NewDecoder(file)
    Configuration = ConfigurationType{}
    err = decoder.Decode(&Configuration)
    if err != nil {
    fmt.Println("Error: ", err)
    }
    fmt.Printf("Configuration: %v\n", Configuration)
    }
    ```
    将读文件的 err 信息打印出来了。

    感谢!
    donething
        3
    donething  
       2019-06-19 13:10:14 +08:00 via Android
    再挑个次要问题,defer file.Close()应该放在 if err != nil 之后
    wewin
        4
    wewin  
    OP
       2019-06-19 14:08:47 +08:00
    @donething 这是为什么呢?`defer` 语句不是在方法返回前触发的吗?
    donething
        5
    donething  
       2019-06-19 15:10:57 +08:00 via Android
    @wewin 如果打开文件若有错误发生,就不需要调用 file.Close(),所以写在 if err!=nil 语句后面。
    wewin
        6
    wewin  
    OP
       2019-06-19 18:57:23 +08:00
    @donething 你的意思是这样?
    ```
    if err != nil {

    }
    ```
    wewin
        7
    wewin  
    OP
       2019-06-19 18:59:09 +08:00
    @donething 你的意思是这样?
    ```
    if err != nil {
    defer file.close()
    }
    ```
    donething
        8
    donething  
       2019-06-19 19:07:23 +08:00 via Android
    if err != nil {
    return err
    }
    defer file.close()
    labulaka
        9
    labulaka  
       2019-06-20 11:07:26 +08:00 via Android
    任何时候别忽略 err
    anonymous256
        10
    anonymous256  
       2019-06-20 23:20:21 +08:00 via Android
    我也很烦 golang 每次都要判断 err,所以一般就写个函数
    func checkErr(err error){
    if err != nil{
    // do something
    }
    }
    这样每次都只直接调用这个 checkErr,而不是判断它是不是等于 nil 了,比较偷懒的写法
    wewin
        11
    wewin  
    OP
       2019-06-24 08:51:40 +08:00
    @donething 不错,经过测试发现自己对 defer 返回前调用理解有误。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1953 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 16:18 · PVG 00:18 · LAX 09:18 · JFK 12:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.