Go 的标准库没有专门用于检查文件是否存在的函数(如 Python 的os.path.exists
)。惯用的方法是什么?
13 回答
检查文件是否不存在,相当于 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
}
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
您应该使用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(),许多人说这是最佳实践,在这里
首先要考虑的是,您很少只想检查文件是否存在。在大多数情况下,如果文件存在,您会尝试对文件执行某些操作。在 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
}
但是,由于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
}
通常最好避免os.Stat
在尝试对文件进行操作之前检查文件是否存在,因为在您执行操作之前的时间窗口内,文件总是有可能被重命名、删除等它。
但是,如果您对这个警告没有意见,并且您真的,真的只是想检查文件是否存在,而不是继续对它做一些有用的事情(作为一个人为的例子,假设您正在编写一个毫无意义的 CLI 工具告诉您文件是否存在然后退出¯\_(ツ)_/¯
),那么推荐的方法是:
if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
// file does not exist
} else {
// file exists
}
其他答案遗漏的是,给函数的路径实际上可能是一个目录。以下函数确保路径确实是一个文件。
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
需要指出的另一件事:此代码仍可能导致竞争条件,其中另一个线程或进程删除或创建指定的文件,而 fileExists 函数正在运行。
如果您担心这一点,请在线程中使用锁,序列化对该函数的访问,或者如果涉及多个应用程序,请使用进程间信号量。如果涉及其他应用程序,超出您的控制范围,我猜您就不走运了。
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
}
_, 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)
}
基本上
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")
}
}
另一种方式
使用 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"))
}
函数示例:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
让我们先看几个方面,os
package of提供的功能golang
都不是实用程序而是错误检查器,我的意思是它们只是一个处理跨平台错误的包装器。
所以基本上,os.Stat
如果这个函数没有给出任何错误,这意味着文件存在,如果它确实需要检查它是什么类型的错误,这里就使用这两个函数os.IsNotExist
和os.IsExist
.
这可以理解为Stat
文件抛出错误,因为它不存在,或者它是因为它存在并且存在一些问题而抛出错误。
这些函数采用的参数是 type error
,尽管您可以将其传递nil
给它,但它没有意义。
这也表明IsExist is not same as !IsNotExist
,它们是两种不同的东西。
因此,现在如果您想知道给定文件是否存在于 go 中,我希望最好的方法是:
if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}
检查文件是否存在的最佳方法:
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
这就是我检查文件是否存在于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")
}
}