如何检查Go中是否存在文件?

Rei*_*ica 393 file standard-library go

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

Rei*_*ica 653

要检查文件是否不存在,等同于Python if not os.path.exists(filename):

if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
  // path/to/whatever does not exist
}
Run Code Online (Sandbox Code Playgroud)

检查文件是否存在,等同于Python if os.path.exists(filename):

编辑:根据最近的评论

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

} else if os.IsNotExist(err) {
  // 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


}
Run Code Online (Sandbox Code Playgroud)

  • 第二个片段更巧妙地错了; 条件应该是`!os.IsNotExist(err)`.文件可能存在但是`os.Stat`由于其他原因而失败(例如,权限,磁盘失败).使用`err == nil`作为条件错误地将此类失败归类为"文件不存在". (41认同)
  • 检查文件是否存在是错误的:如果文件存在,则err为nil (9认同)
  • 最新的文档告诉您在任何新代码中使用“errors.Is(err, os.ErrNotExist)”:https://github.com/golang/go/blob/master/src/os/error.go#L90- L91 (4认同)
  • 有时它返回ENOTDIR而不是`NOTEXIST`,例如,如果存在`/ etc/bashrc`,`/ etc/bashrc/foobar`将返回`ENOTDIR` (3认同)
  • 确保展开 ~ 否则它将返回 false... /sf/ask/1232681271/#43578461 (2认同)

Osc*_*Ryz 116

Caleb Spare的回答发表在gonuts邮件列表中.

[...]实际上并不经常需要os.Stat它,并且在需要的情况下使用它很容易.

[...]例如:如果要打开文件,则没有理由先检查它是否存在.检查和打开之间文件可能会消失,无论如何,无论如何都需要检查os.Open错误.因此,您只需os.IsNotExist(err)在尝试打开文件后调用,并在那里处理它不存在(如果需要特殊处理).

[...]您根本不需要检查存在的路径(您不应该).

  • os.MkdirAll无论路径是否已存在,均可正常工作.(您还需要检查该呼叫的错误.)

  • 而不是使用os.Create,你应该使用os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666).这样,如果文件已存在,您将收到错误.此外,没有其他制作文件的竞争条件,与预先检查存在的版本不同.

取自:https://groups.google.com/forum/#! msg/golang -nuts/Ayx-BMNdMFo/4rL8FFHr8v4J


小智 29

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

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

Run Code Online (Sandbox Code Playgroud)

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

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


小智 28

您应该使用以下示例中的os.Stat()os.IsNotExist()函数:

// Exists reports whether the named file or directory exists.
func Exists(name string) bool {
    if _, err := os.Stat(name); err != nil {
        if os.IsNotExist(err) {
            return false
        }
    }
    return true
}
Run Code Online (Sandbox Code Playgroud)

这个例子是从这里提取的.

  • 注意:正如http://stackoverflow.com/a/22467409/712014指出的那样,即使文件不存在,此代码也会返回true,例如当Stat()返回权限被拒绝时. (10认同)

Dav*_*ood 26

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

过去推荐的方法

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

但是,由于errors.Is在 Go 1.13(2019 年末发布)中添加了,新的建议是使用errors.Is

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

通常最好避免os.Stat在尝试使用文件之前检查文件是否存在,因为在您使用之前的时间窗口中,文件总是有可能被重命名、删除等。它。

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

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

  • 除海滩外,鲨鱼袭击事件的情况很少见。除了标题为“如何检查 Go 中文件是否存在”的问题外,很少需要专门检查文件是否存在。就我而言,如果文件存在,我的工作就完成了;如果它不存在,我必须生成它。 (3认同)

小智 16

user11617示例不正确; 它会报告文件存在,即使它没有,但有一些其他类型的错误.

签名应该是Exists(字符串)(bool,错误).然后,实际上,呼叫站点并没有更好.

他写的代码更好:

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

但我建议这样做:

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

  • 例5是什么?你能具体一点吗? (7认同)
  • 为什么返回`err!= nil`而不是`err == nil`?如果有错误,则文件可能不存在? (5认同)

tan*_*nfa 11

    _, 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)
    }
Run Code Online (Sandbox Code Playgroud)


dıl*_*ücü 8

基本上


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 Code Online (Sandbox Code Playgroud)

运行示例

另一种方式

使用 os.Open

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 Code Online (Sandbox Code Playgroud)

运行


Far*_*hsh 7

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

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

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

这些函数采用的参数是 type error,尽管您可以将其传递nil给它,但这没有意义。

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

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

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


Roe*_*mer 7

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

// 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
}
Run Code Online (Sandbox Code Playgroud)


hon*_*ple 6

功能示例:

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

  • if 不是多余的吗? (2认同)

小智 6

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

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


Seb*_*n N 6

正如其他答案中提到的,可以通过使用不同的标志来构建所需的行为/错误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)
}
Run Code Online (Sandbox Code Playgroud)

您应该自己组合这些标志以获得您感兴趣的行为:

// 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.
)
Run Code Online (Sandbox Code Playgroud)

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

下面是一个示例,它要么截断现有文件,要么在文件存在时失败。

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
Run Code Online (Sandbox Code Playgroud)


小智 5

这就是我检查文件是否存在于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")
    }
}
Run Code Online (Sandbox Code Playgroud)