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


435

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


我真的不明白。同时,您说没有标准功能,并使用标准功能编写答案。我想念什么?至少这个问题应该解决吗?
DenysSéguret2012年

@dystroy-修复了问题。
Sridhar Ratnakumar 2012年

11
最好避免查询文件的存在。B / c是答案的活泼性,所获得的信息说,在要求的时间内,文件上方实际上没有任何有用的东西-但可能不再存在。推荐的方法是简单地打开一个文件,然后检查该文件是否失败。
zzzz 2012年

2

2
@zzzz(我知道已经好几年了,此评论适用于新读者)我同意一般情况。但是我的应用程序加载了一个第三方库,该库将某些文件路径作为初始化数据,但是如果该文件不存在,则会出现段错误。我认为这是检查文件是否存在而无需尝试打开该文件即可报告错误而不会导致致命崩溃的有效方案,因为我的代码不需要读取文件内容或直接写入文件。
塞尔吉奥·阿科斯塔

Answers:


691

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

if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
  // 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 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


}

3
有时它返回ENOTDIR而不是NOTEXIST,例如,如果/etc/bashrc存在,/etc/bashrc/foobar遗嘱将返回ENOTDIR
lidaobing 2013年

43
第二个片段更微妙。条件应该是!os.IsNotExist(err)。该文件可能存在,但os.Stat由于其他原因而失败(例如,权限,磁盘故障)。err == nil作为条件使用错误地将失败分类为“文件不存在”。
平方英尺2015年

9
检查文件是否存在错误:如果文件存在,则err为nil
tangxinfa

1
确保扩大〜否则将返回false ... stackoverflow.com/questions/17609732/...
马塞罗日销售

您可以根据情况使用os.IsExist(),在做!os.IsNotExistant()时可能更惯用,而不是双重否定
Ariel Monaco

126

Caleb Spare的回答张贴在甜甜圈邮件列表中。

[...]实际上并不经常需要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


30

您应该在以下示例中使用os.Stat()and 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
}

该示例从此处提取。


12
当心:正如stackoverflow.com/a/22467409/712014所指出的,即使文件不存在,此代码也会返回true,例如,当Stat()返回权限被拒绝时。
迈克尔

19

user11617示例不正确;它将报告该文件存在,即使在不存在的情况下,也存在其他错误。

签名应为Exists(string)(布尔,错误)。然后,碰巧的是,呼叫站点并没有更好。

他编写的代码最好是:

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
}

7
示例5是什么?请您具体说一下。
xlm 2014年

1
您的第二个示例需要解构多个返回值-例如_,err:= os.Stat(name)
David Duncan

6
为什么要返回err != nil而不是err == nil?如果有错误,则文件可能不存在?
idbrii

14

其他答案遗漏的是,赋予该函数的路径实际上可能是目录。跟随功能可确保该路径确实是一个文件。

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

需要指出的另一点是:该代码仍可能导致争用情况,在fileExists函数运行时,另一个线程或进程删除或创建了指定的文件。

如果您对此感到担心,请在线程中使用锁,序列化对此函数的访问,或者如果涉及多个应用程序,请使用进程间信号灯。我想,如果涉及其他应用程序,那么您将无法控制。


12
    _, 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)
    }

7

函数示例:

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

1
如果不是多余的话吗?
伊利亚·乔利

6

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

因此,基本上,os.Stat如果该函数没有给出任何错误,则意味着如果该文件存在,则需要检查它是什么类型的错误,这时将使用这两个函数os.IsNotExistos.IsExist

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

这些函数采用的参数的类型为error,尽管您可以传递nil给它,但这没有任何意义。

这也指出了一个事实IsExist is not same as !IsNotExist,它们是两种不同的方式。

因此,现在如果您想知道给定文件是否存在,我希望最好的方法是:

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

1

如其他答案中所述,可以通过使用带有的不同标志来构造所需的行为/错误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.
)

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

这是一个我希望打开文件进行写入的示例,但是如果用户说可以的话,我只会截断现有文件:

var f *os.File
if truncateWhenExists {
    // O_TRUNC - truncate regular writable file when opened.
    if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
        log.Fatalln("failed to force-open file, err:", err)
    }
} else {
    // O_EXCL - used with O_CREATE, file must not exist
    if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644); err != nil {
        log.Fatalln("failed to open file, err:", err) 
   }
}

0

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

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
    // your code here if file exists
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.