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

Posted

技术标签:

【中文标题】如何检查Go中是不是存在文件?【英文标题】:How to check if a file exists in Go?如何检查Go中是否存在文件? 【发布时间】:2012-09-13 04:31:15 【问题描述】:

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

【问题讨论】:

我不太明白。同时你说没有标准函数,你用标准函数写了一个答案。我错过了什么?至少问题不应该解决吗? @dystroy - 解决了这个问题。 最好避免查询文件是否存在。 B/c 关于答案的活泼性质,所获得的信息实际上表明,在所要求的时间内,文件上方没有任何有用的信息——但它可能不再存在。推荐的方法是简单地打开一个文件并检查它是否失败。 @zzzz(我知道已经有好几年了,这个评论是给新读者的)我同意一般情况。但是我的应用程序加载了一个第三方库,该库将一些文件路径作为初始化数据,但如果该文件不存在,则会出现段错误。我认为这是检查文件是否存在而不试图打开它以报告错误而不发生致命崩溃的有效方案,因为我的代码不需要读取文件内容或直接写入文件。跨度> 【参考方案1】:

检查文件是否不存在,相当于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):

已编辑:根据最近的 cmets

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



【讨论】:

有时它返回ENOTDIR而不是NOTEXIST,例如,如果/etc/bashrc存在,/etc/bashrc/foobar将返回ENOTDIR 第二个sn-p更微妙的错误;条件应该是!os.IsNotExist(err)。文件可能存在,但 os.Stat 由于其他原因(例如权限、磁盘故障)而失败。使用err == nil 作为条件错误地将此类故障归类为“文件不存在”。 检查文件是否存在错误:如果文件存在则err为nil 一定要展开~否则会返回false...***.com/questions/17609732/… 最新的文档告诉你在任何新代码中使用errors.Is(err, os.ErrNotExist):github.com/golang/go/blob/master/src/os/error.go#L90-L91【参考方案2】:

Caleb Spare 的回答已发布在 gonuts 邮件列表中。

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

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

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

os.MkdirAll 无论路径是否已经存在都有效。 (您还需要检查该调用的错误。)

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

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

【讨论】:

【参考方案3】:

您应该使用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(),很多人说是 best-practice 和 here

【讨论】:

注意:正如***.com/a/22467409/712014 指出的那样,即使文件不存在,此代码也会返回true,例如当Stat() 返回权限被拒绝时。【参考方案4】: 推荐的答案 Go Language

首先要考虑的是,您很少只想检查文件是否存在。在大多数情况下,如果文件存在,您会尝试对文件执行某些操作。在 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

但是,自从在 Go 1.13 中添加了errors.Is(2019 年底发布),the new recommendation is to use 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

【讨论】:

我有一个问题,你怎么知道使用这种方法是文件还是目录? 假设您正在谈论(不推荐!)os.Stat 方法:os.Stat 返回 info, err,如果 err 为零,info 是一个 FileInfo 对象,您可以用于确定存在的东西是文件还是目录。有关详细信息,请参阅this other answer。 不,我问的是推荐的方法file, err := os.OpenFile(...) if errors.Is(err, os.ErrNotExist) 当你已经有了文件时这很好,但有时你会得到一个目录或文件,如果一个文件你做某事,如果没有,并且是一个目录然后你做一些其他的。有 100000 个使用 os.Stat 的示例,您提到我们应该避免使用它,这就是我问的原因。如何使用推荐的方法并检查是否是 dir 或处理这种情况。 好问题。我不确定,如果您在目录上调用 OpenFile 会发生什么。如果它返回错误,那么我认为在大多数情况下,以与处理任何其他错误相同的方式处理错误就足够了。【参考方案5】:

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

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


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

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

【讨论】:

如果涉及其他应用程序,您可能希望将文件保存在单独的文件夹中。如果您正在创建新文件,O_EXCL 是您避免竞争的朋友(如果您使用的平台支持该标志)。 不要使用这个原因info在调用return !info.IsDir()时可能为nil【参考方案6】:

example by 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

【讨论】:

示例 5 是什么?能具体点吗。 您的第二个示例需要解构多个返回值 - 例如_, err := os.Stat(name) 为什么返回 err != nil 而不是 err == nil ?如果有错误,那么文件可能不存在?【参考方案7】:
    _, 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)
    

【讨论】:

【参考方案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 example

其他方式

使用 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 it

【讨论】:

【参考方案9】:

函数示例:

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

【讨论】:

if 不是多余的吗? 不是,但目前逻辑已被破坏。如果文件不存在,该函数应该只返回 false,但是目前它在任何错误时返回 false。可能它应该恐慌。此外,“文件存在”是英文不好。【参考方案10】:

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

所以基本上如果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
 

【讨论】:

【参考方案11】:

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

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

【讨论】:

【参考方案12】:

正如其他答案中提到的,可以通过使用 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

【讨论】:

【参考方案13】:

这是我检查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")
    

【讨论】:

以上是关于如何检查Go中是不是存在文件?的主要内容,如果未能解决你的问题,请参考以下文章

golang go中存在检查文件

如何检查文件夹中是不是存在文件?

如何检查python中是不是存在文件? [复制]

Shell如何检查文件中的一行中是不是存在模式

如何检查该文件是不是存在于 zip 存档中?

如何检查文件路径是不是存在