Стандартная библиотека Go не имеет функции, предназначенной исключительно для проверки того, существует файл или нет (как в Python os.path.exists
). Какой идиоматический способ сделать это?
Стандартная библиотека Go не имеет функции, предназначенной исключительно для проверки того, существует файл или нет (как в Python os.path.exists
). Какой идиоматический способ сделать это?
Ответы:
Чтобы проверить, не существует ли файл, эквивалентный 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
}
NOTEXIST
, например, если он /etc/bashrc
существует, /etc/bashrc/foobar
возвращаетсяENOTDIR
!os.IsNotExist(err)
. Возможно, файл существует, но os.Stat
не работает по другим причинам (например, разрешение, сбой диска). Использование err == nil
в качестве условия неправильно классифицирует такие сбои как «файл не существует».
Ответ от 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()
функцию , как в следующем примере:
// 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
}
Пример извлечен отсюда .
Пример по 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 != nil
вместо err == nil
? Если есть ошибка, то файл, вероятно, не существует?
Что пропустили другие ответы, так это то, что путь к функции может быть каталогом. Следующая функция гарантирует, что путь действительно является файлом.
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
Еще один момент, на который следует обратить внимание: этот код все еще может привести к состоянию гонки, когда другой поток или процесс удаляет или создает указанный файл во время работы функции fileExists.
Если вас это беспокоит, используйте блокировку в своих потоках, сериализуйте доступ к этой функции или используйте межпроцессный семафор, если задействованы несколько приложений. Думаю, если другие приложения задействованы вне вашего контроля, вам не повезло.
Давайте сначала рассмотрим несколько аспектов. Обе функции, предоставляемые os
пакетом, - golang
это не утилиты, а средства проверки ошибок. Я имею в виду, что они являются просто оболочкой для обработки ошибок на кроссплатформенной платформе.
Таким образом, в основном, os.Stat
если эта функция не выдает никакой ошибки, что означает, что файл существует, если вам нужно проверить, что это за ошибка, то здесь следует использовать эти две функции os.IsNotExist
и os.IsExist
.
Это может быть понято как Stat
ошибка выброса файла, потому что он не существует, или это ошибка выброса, потому что он существует, и есть некоторая проблема с ним.
Параметр, который принимают эти функции, имеет тип error
, хотя вы можете передать nil
его, но это не имеет смысла.
Это также указывает на тот факт IsExist is not same as !IsNotExist
, что это две разные вещи.
Так что теперь, если вы хотите знать, существует ли данный файл в go, я бы предпочел лучший способ:
if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}
Как уже упоминалось в других ответах, можно построить требуемое поведение / ошибки, используя различные флаги с 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)
}
}