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

huangapple go评论95阅读模式
英文:

How to check if a file exists in Go?

问题

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

英文:

Go's standard library does not have a function solely intended to check if a file exists or not (like Python's os.path.exists). What is the idiomatic way to do it?

答案1

得分: 1019

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

if _, err := os.Stat("/path/to/whatever"); errors.Is(err, os.ErrNotExist) {
  // path/to/whatever 不存在
}

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

if _, err := os.Stat("/path/to/whatever"); err == nil {
  // path/to/whatever 存在
} else if errors.Is(err, os.ErrNotExist) {
  // path/to/whatever *不存在*
} else {
  // 薛定谔的猫:文件可能存在也可能不存在。查看err获取详细信息。

  // 因此,*不要*使用!os.IsNotExist(err)来测试文件是否存在
}
英文:

To check if a file doesn't exist, equivalent to Python's if not os.path.exists(filename):

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

To check if a file exists, equivalent to Python's if os.path.exists(filename):

Edited: per recent comments

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


}

答案2

得分: 157

[...] 它并不经常需要,[...] 对于需要的情况,使用 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

英文:

Answer by Caleb Spare posted in gonuts mailing list.

> [...] It's not actually needed very often and [...] using os.Stat is
easy enough for the cases where it is required.

> [...] For instance: if you are going to open the file, there's no reason to check whether it exists first. The file could disappear in between checking and opening, and anyway you'll need to check the os.Open error regardless. So you simply call os.IsNotExist(err) after you try
to open the file, and deal with its non-existence there (if that requires special handling).

> [...] You don't need to check for the paths existing at all (and you shouldn't).

> - os.MkdirAll works whether or not the paths already exist. (Also you need to check the error from that call.)

> - Instead of using os.Create, you should use os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) . That way you'll get an error if the file already exists. Also this doesn't have a race condition with something else making the file, unlike your version which checks for existence beforehand.

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

答案3

得分: 68

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

过去的推荐方法是:

file, err := os.OpenFile(...)
if os.IsNotExist(err) {
    // 处理文件不存在的情况
}

然而,自从Go 1.13(于2019年底发布)添加了errors.Is之后,新的推荐方法是使用errors.Is

file, err := os.OpenFile(...)
if errors.Is(err, os.ErrNotExist) {
    // 处理文件不存在的情况
}

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

然而,如果你可以接受这个注意事项,并且你真的只想检查文件是否存在,而不打算继续对其进行有用的操作(作为一个假设的例子,假设你正在编写一个无意义的命令行工具,它告诉你文件是否存在,然后退出 ¯\_(ツ)_/¯),那么推荐的方法是:

if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
    // 文件不存在
} else {
    // 文件存在
}
英文:

The first thing to consider is that it is rare that you would only want to check whether or not a file exists. In most situations, you're trying to do something with the file if it exists. In Go, any time you try to perform some operation on a file that doesn't exist, the result should be a specific error (os.ErrNotExist) and the best thing to do is check whether the return err value (e.g. when calling a function like os.OpenFile(...)) is os.ErrNotExist.

The recommended way to do this used to be:

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

However, since the addition of errors.Is in Go 1.13 (released in late 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
}

It's usually best to avoid using os.Stat to check for the existence of a file before you attempt to do something with it, because it will always be possible for the file to be renamed, deleted, etc. in the window of time before you do something with it.

However, if you're OK with this caveat and you really, truly just want to check whether a file exists without then proceeding to do something useful with it (as a contrived example, let's say that you're writing a pointless CLI tool that tells you whether or not a file exists and then exits ¯\_(ツ)_/¯), then the recommended way to do it would be:

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

答案4

得分: 55

你应该使用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
}

编辑1:修复了在某些情况下返回true的问题。
编辑2:从os.IsNotExist()切换到使用errors.Is(),许多人认为这是一种最佳实践,这里这里有相关信息。

英文:

You should use the os.Stat() and os.IsNotExist() functions as in the following example:

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: fixed issue of returning true when under some circumstances.
edit2: switched to using errors.Is() from os.IsNotExist(), which many say is a best-practice and here

答案5

得分: 30

以下是翻译好的部分:

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

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

如果你担心这个问题,可以在线程中使用锁,序列化对该函数的访问,或者如果涉及多个应用程序,则使用进程间信号量。如果涉及到其他应用程序且不在你的控制范围内,那就没办法了,我猜。

英文:

What other answers missed, is that the path given to the function could actually be a directory. Following function makes sure, that the path is really a file.

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

Another thing to point out: This code could still lead to a race condition, where another thread or process deletes or creates the specified file, while the fileExists function is running.

If you're worried about this, use a lock in your threads, serialize the access to this function or use an inter-process semaphore if multiple applications are involved. If other applications are involved, outside of your control, you're out of luck, I guess.

答案6

得分: 23

The example by user11617 is incorrect; it will report that the file exists even in cases where it does not, but there was an error of some other sort.

The signature should be Exists(string) (bool, error). And then, as it happens, the call sites are no better.

The code he wrote would better as:

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

But I suggest this instead:

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

The example by user11617 is incorrect; it will report that the file exists even in cases where it does not, but there was an error of some other sort.

The signature should be Exists(string) (bool, error). And then, as it happens, the call sites are no better.

The code he wrote would better as:

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

But I suggest this instead:

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

答案7

得分: 13

_, err := os.Stat(file)
if err == nil {
log.Printf("文件 %s 存在", file)
} else if os.IsNotExist(err) {
log.Printf("文件 %s 不存在", file)
} else {
log.Printf("文件 %s 状态错误: %v", file, 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)
	}

答案8

得分: 10


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("文件存在")
	} else {

		fmt.Println("文件不存在")
	}

}

运行示例

另一种方法

使用os.Open

package main

import (
	"fmt"
	"os"
)

func fileExists(path string) bool {
	_, err := os.Open(path) // 用于读取访问权限。
	return err == nil

}

func main() {

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

}


运行示例

英文:

basicly


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

other way

with 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

得分: 9

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

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
	// 如果文件存在,在这里编写你的代码
}
英文:

Best way to check if file exists:

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

答案10

得分: 8

函数示例:

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

The function example:

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

答案11

得分: 8

让我们首先看一下几个方面,golangos 包提供的函数不是工具函数,而是错误检查器,我的意思是它们只是一个处理跨平台错误的包装器。

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

这可以理解为文件的 Stat 函数抛出错误是因为它不存在,还是因为它存在但有问题。

这些函数接受的参数类型是 error,虽然你可能能够传递 nil 给它,但这没有意义。

这也说明了 IsExist 不等于 !IsNotExist,它们是完全不同的东西。

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

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

Let's look at few aspects first, both the function provided by os package of golang are not utilities but error checkers, what do I mean by that is they are just a wrapper to handle errors on cross platform.

So basically if os.Stat if this function doesn't give any error that means the file is existing if it does you need to check what kind of error it is, here comes the use of these two function os.IsNotExist and os.IsExist.

This can be understood as the Stat of the file throwing error because it doesn't exists or is it throwing error because it exist and there is some problem with it.

The parameter that these functions take is of type error, although you might be able to pass nil to it but it wouldn't make sense.

This also points to the fact that IsExist is not same as !IsNotExist, they are way two different things.

So now if you want to know if a given file exist in go, I would prefer the best way is:

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

答案12

得分: 7

如其他答案中所提到的,可以通过使用os.OpenFile的不同标志来构建所需的行为/错误。实际上,os.Create只是一个使用合理默认值的快捷方式:

// Create创建或截断指定的文件。如果文件已经存在,则截断它。如果文件不存在,则以0666模式(在umask之前)创建它。如果成功,返回的File上的方法可用于I/O;关联的文件描述符具有O_RDWR模式。如果出现错误,它将是*PathError类型。
func Create(name string) (*File, error) {
	return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

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

// OpenFile的标志,包装底层系统的标志。并非所有标志都可能在给定系统上实现。
const (
	// 必须指定O_RDONLY、O_WRONLY或O_RDWR中的一个。
	O_RDONLY int = syscall.O_RDONLY // 以只读方式打开文件。
	O_WRONLY int = syscall.O_WRONLY // 以只写方式打开文件。
	O_RDWR   int = syscall.O_RDWR   // 以读写方式打开文件。
	// 可以通过或运算来控制行为的其余值。
	O_APPEND int = syscall.O_APPEND // 写入时将数据追加到文件。
	O_CREATE int = syscall.O_CREAT  // 如果不存在,则创建新文件。
	O_EXCL   int = syscall.O_EXCL   // 与O_CREATE一起使用,文件必须不存在。
	O_SYNC   int = syscall.O_SYNC   // 用于同步I/O的打开。
	O_TRUNC  int = syscall.O_TRUNC  // 打开时截断常规可写文件。
)

根据您的选择,将获得不同的错误。

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

openOpts := os.O_RDWR|os.O_CREATE
if truncateWhenExists {
    openOpts |= os.O_TRUNC // 文件将被截断
} else {
    openOpts |= os.O_EXCL  // 文件不得存在
}
f, err := os.OpenFile(filePath, openOpts, 0644)
// ... 做一些操作
英文:

As mentioned in other answers, it is possible to construct the required behaviour / errors from using different flags with os.OpenFile. In fact, os.Create is just a sensible-defaults shorthand for doing so:

// 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)
}

You should combine these flags yourself to get the behaviour you are interested in:

// 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.
)

Depending on what you pick, you will get different errors.

Below is an example which will either truncate an existing file, or fail when a file exists.

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

得分: 6

这是我对文件存在方法的看法。它还检查文件不是一个目录,并在出现错误的情况下返回错误。

// FileExists检查文件是否存在(且不是目录)。
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
}
英文:

Here is my take on a file exists method. It also checks that the file is not a directory and in case of an error, returns it as well.

// 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
}

答案14

得分: 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("文件存在")
	}
}
英文:

This is how I check if a file exists in 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")
	}
}

huangapple
  • 本文由 发表于 2012年9月21日 02:42:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/12518876.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定