如何检查一个文件是否存在于Go?

Go的标准库没有专门用于检查文件是否存在的函数(如Python的os.path.exists)。惯用是怎么做的?

425114 次浏览

检查文件是否不存在,相当于Python的if not os.path.exists(filename):

if _, err := os.Stat("/path/to/whatever"); errors.Is(err, os.ErrNotExist) {
// path/to/whatever does not exist
}

检查一个文件是否存在,相当于Python的if os.path.exists(filename):

编辑:根据最近的评论

if _, err := os.Stat("/path/to/whatever"); err == nil {
// path/to/whatever exists


} else if errors.Is(err, os.ErrNotExist) {
// path/to/whatever does *not* exist


} else {
// Schrodinger: file may or may not exist. See err for details.


// Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence




}

您应该使用os.Stat()os.IsNotExist()函数,示例如下:

func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if err == nil {
return true, nil
}
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, err
}

edit1:修复在某些情况下返回true的问题。
edit2:从os.IsNotExist()切换到errors.Is(),许多人说这是最佳实践在这里

user11617创建的示例不正确;即使在文件不存在的情况下,它也会报告文件存在,但存在其他类型的错误。

签名应该是Exists(string) (bool, error)。然后,碰巧的是,通话地点也没好到哪里去。

他编写的代码最好是:

func Exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}

但我建议这样做:

func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if os.IsNotExist(err) {
return false, nil
}
return err != nil, err
}

迦勒备用gonuts邮件列表中回复。

< p >[…实际上并不经常需要,而且[…]使用os.Stat是 对于需要它的情况来说很简单。< / p > < p >[…例如:如果你要打开文件,没有理由先检查它是否存在。文件可能会在检查和打开之间消失,无论如何您都需要检查os.Open错误。因此,您只需在尝试后调用os.IsNotExist(err) 打开文件,并处理它不存在的问题(如果需要特殊处理的话)。< / p >

[…你根本不需要检查是否存在路径(你也不应该)。

  • 不管路径是否已经存在,os.MkdirAll都有效。(此外,您还需要检查该调用的错误。)

  • 您应该使用os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666),而不是os.Create。这样,如果文件已经存在,您将得到一个错误。此外,它没有与其他文件生成的竞争条件,不像您的版本预先检查是否存在。

取自:https://groups.google.com/forum/ !味精rl8ffhr8v4j / golang-nuts / Ayx-BMNdMFo / 4

    _, err := os.Stat(file)
if err == nil {
log.Printf("file %s exists", file)
} else if os.IsNotExist(err) {
log.Printf("file %s not exists", file)
} else {
log.Printf("file %s stat error: %v", file, err)
}

函数示例:

func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}

让我们先看几个方面,os包和golang包提供的函数都不是实用程序,而是错误检查器,我的意思是它们只是跨平台处理错误的包装器。

所以基本上,如果os.Stat如果这个函数没有给出任何错误,那就意味着文件存在,如果它有,你需要检查它是什么样的错误,这里有两个函数os.IsNotExistos.IsExist的使用。

这可以理解为文件抛出错误的Stat,因为它不存在,或者它抛出错误是因为它存在并且有一些问题。

这些函数接受的参数类型是error,尽管您可以将nil传递给它,但这没有意义。

这也指出了IsExist is not same as !IsNotExist,它们是两种不同的东西。

所以现在如果你想知道一个给定的文件是否存在于go中,我想最好的方法是:

if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}

其他答案忽略的是,给函数的路径实际上可能是一个目录。下面的函数确保,路径确实是一个文件。

func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}


另一件需要指出的事情是:这段代码仍然可能导致竞态条件,即在fileExists函数运行时,另一个线程或进程删除或创建指定的文件。

如果您担心这一点,可以在线程中使用锁,序列化对该函数的访问,或者在涉及多个应用程序时使用进程间信号量。如果涉及到其他应用程序,超出了您的控制范围,我想您就不走运了。

检查文件是否存在的最佳方法:

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
// your code here if file exists
}

正如在其他答案中提到的,可以使用os.OpenFile使用不同的标志来构建所需的行为/错误。事实上,os.Create只是这样做的一个合理的默认值简写:

// Create creates or truncates the named file. If the file already exists,
// it is truncated. If the file does not exist, it is created with mode 0666
// (before umask). If successful, methods on the returned File can
// be used for I/O; the associated file descriptor has mode O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

你应该自己结合这些标志来获得你感兴趣的行为:

// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR   int = syscall.O_RDWR   // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
)

根据您选择的内容,您将得到不同的错误。

下面是一个示例,它将截断一个现有文件,或者当文件存在时失败。

openOpts := os.O_RDWR|os.O_CREATE
if truncateWhenExists {
openOpts |= os.O_TRUNC // file will be truncated
} else {
openOpts |= os.O_EXCL  // file must not exist
}
f, err := os.OpenFile(filePath, openOpts, 0644)
// ... do stuff

基本上


package main


import (
"fmt"
"os"
)


func fileExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}


func main() {


var file string = "foo.txt"
exist := fileExists(file)
    

if exist {
fmt.Println("file exist")
} else {


fmt.Println("file not exists")
}


}


run example

另一种方法

与操作系统。开放

package main


import (
"fmt"
"os"
)


func fileExists(path string) bool {
_, err := os.Open(path) // For read access.
return err == nil


}


func main() {


fmt.Println(fileExists("d4d.txt"))


}




run it

首先要考虑的是,您很少只想检查一个文件是否存在。在大多数情况下,如果文件存在,您将尝试对它做一些事情。在Go中,每当你试图对一个不存在的文件执行一些操作时,结果应该是一个特定的错误(os.ErrNotExist),最好的方法是检查返回的err值是否为os.ErrNotExist(例如在调用os.OpenFile(...)这样的函数时)。

推荐的方法是曾经是:

file, err := os.OpenFile(...)
if os.IsNotExist(err) {
// handle the case where the file doesn't exist
}

然而,因为在Go 1.13中添加了errors.Is(2019年底发布),新的建议是使用errors.Is:

file, err := os.OpenFile(...)
if errors.Is(err, os.ErrNotExist) {
// handle the case where the file doesn't exist
}

在尝试对文件进行操作之前,通常最好避免使用os.Stat来检查文件是否存在,因为在对文件进行操作之前,总是有可能在窗口时间内对文件进行重命名、删除等操作。

然而,如果你可以接受这个警告,你真的,真的只是想检查一个文件是否存在,而不是继续对它做一些有用的事情(作为一个人为的例子,假设你正在编写一个毫无意义的CLI工具,告诉你一个文件是否存在,然后退出¯\_(ツ)_/¯),那么推荐的方法是:

if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
// file does not exist
} else {
// file exists
}

这是我如何检查文件是否存在Go 1.16

package main


import (
"errors"
"fmt"
"io/fs"
"os"
)


func main () {
if _, err:= os.Stat("/path/to/file"); errors.Is(err, fs.ErrNotExist){
fmt.Print(err.Error())
} else {
fmt.Print("file exists")
}
}

这是我对一个文件存在方法的看法。它还检查文件是否为目录,如果出现错误,也会返回该文件。

// FileExists checks if a file exists (and it is not a directory).
func FileExists(filePath string) (bool, error) {
info, err := os.Stat(filePath)
if err == nil {
return !info.IsDir(), nil
}
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, err
}