在被覆盖的变量上延迟文件关闭

我正在学习 Go,所以我尝试编写一个应用,从 JSON API 获取一些数据并将其放入文件中。我写了一个函数来检查我的文件是否存在,如果不存在,则创建它。


func ensureFileExists(filePath string) {

    f, err := os.Open(storageFile)

    defer func() {

        err := f.Close()

        if err != nil {

            fmt.Printf("fail to close file %q, error: %q", filePath, err)

            return

        }

        fmt.Printf("file %q closed", filePath)

    }()

    if err != nil {

        if os.IsNotExist(err) {

            f, err = os.Create(storageFile)

            if err != nil {

                panic(err)

            }

            return

        }

        panic(err)

    }

}

我的考虑是:

  1. 这是正确的方法吗?

  2. 当文件未打开(由 ,, ...)时调用是一件坏事,我应该只在错误检查后调用它吗?defer f.Close()OpenCreate

  3. 这与上一个问题有点相关,假设调用中的文件不需要关闭,因为返回了错误,因此无需将结果分配给新变量并单独关闭它,对吗?os.Openos.Create

  4. 当失败时该怎么办,除了放置一些日志或忽略它之外,还有什么可做的吗?f.Close()


幕布斯6054654
浏览 73回答 1
1回答

MMMHUHU

我会说你的方法中有多个“错误”。首先,如果将函数定义为与其他值一起返回错误,则几乎总是应首先检查错误,并且仅在没有错误时才尝试使用其他值。一个众所周知(但很少见)的异常是 和 接口的方法,它可能返回读取/写入的非零字节数和非错误。io.Readerio.Writernil虽然延迟调用不使用立即分配到 的值,但如果调用失败,因此返回非错误,则分配给 的值实际上是未定义的。好吧,Go不是C,并且要真正让它在真正未初始化的内存上运行,必须花费大量时间(并使用),但最重要的事实是,大多数具有多个返回值的函数之一是,不记录任何状态,如果不是,则其余值将是。特别是,当第二个值是非 .fos.Opennilfunsafeerrorerrornilos.Filenilerror好吧,细心的程序员通常不会做愚蠢的事情,实际上当它的第二个,,,不是时,它作为它的第一个返回值返回。但是想想如果你的调用失败会发生什么:变量被分配了值,然后对该变量上关闭的函数文本的延迟调用将尝试调用该值。os.Opennilerrornilos.OpenfnilOpennil同样,在指针接收器上定义的一些方法知道当他们的接收器是 时该怎么办,但不是其中之一,它只会在尝试取消引用指针时爆炸。nilOpennil是的,您似乎通过随后的调用来“补偿”这一点,不允许通过使用失败,但这只会创建复杂的代码。我认为你已经想出了这个解决方案,以便不写两个块 - 一个用于成功者,另一个 - 为成功者,但是如果我是你,我只是写了一个简单的“打开或创建”帮助程序,它将返回与或执行相同的值。不管是不是,Go已经有了一个 - 继续阅读;-)os.Createpanicdeferos.Openos.Createos.Openos.Create因此,大多数时候正确的使用模式是f, err := os.Open(...)if err != nil {  // Handle error  return ...}// At this point f is known to be in a good statedefer func() {  err := f.Close()  // ...}()其次,不需要采用尝试开放然后创建的方法:和 可以看作是广义操作系统的简化接口。OpenFile(它非常接近POSIX的open(2)调用。使用标志,该函数将自动创建文件(如果它不存在),并且作为奖励,这在检查方面以原子方式发生(而您的方法与文件系统有自然的竞争:在尝试打开文件和尝试创建文件之间,其他一些过程可能会创建它,使第二次调用失败)。os.Openos.CreateO_CREATE至于你的最后一个问题,答案是“视情况而定”:如果打开文件进行读取,并且您已成功从文件中读取所有(必需的)数据,则在关闭它时出错并不意味着您丢失了任何内容,实际上不太可能发生。在大多数情况下,将其记录为警告并继续是可以的。如果打开文件进行写入,则无法关闭它可能意味着您可能丢失了在调用 之前写入该文件的部分内容。调用失败的一个常见示例是驻留在网络文件系统(如 NFS 或 CIFS)上的文件。究竟应该采用什么策略在很大程度上取决于执行该操作的进程的性质:例如,如果您正在编写电子邮件服务器,则未能存储消息应导致放弃并将问题正确传达给发送客户端;如果您正在编写交互式应用程序,您可能会询问用户该做什么,并可能允许他们重试或更改文件的位置,然后重试或其他操作。CloseClose
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go