Стандартна бібліотека 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 (рядок) (bool, помилка). І тоді, як це відбувається, сайти для викликів не кращі.
Код, який він написав, краще:
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 працює.
Якщо ви переживаєте з цього приводу, використовуйте блокування в потоках, серіалізуйте доступ до цієї функції або використовуйте міжпроцесорний семафор, якщо задіяно кілька додатків. Якщо задіяні інші додатки, поза вашим контролем, ви, мабуть, не пощастили.
Приклад функції:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
Розглянемо спочатку декілька аспектів, обидва функції, що надаються os
пакетом, - golang
це не утиліти, а перевірки помилок. Що я маю на увазі під тим, що вони просто обгортка для обробки помилок на крос-платформі.
Отже, os.Stat
якщо ця функція не дає жодної помилки, що означає, що файл існує, якщо вам потрібно перевірити, що це за помилка, тут приходить використання цих двох функцій os.IsNotExist
і os.IsExist
.
Це можна зрозуміти як Stat
помилку під час перекидання файлів, оскільки її немає, або помилку викидання, оскільки вона існує, і є з цим певна проблема.
Параметр, який приймають ці функції, має тип error
, хоча ви можете перейти nil
до нього, але це не має сенсу.
Це також вказує на той факт, що IsExist is not same as !IsNotExist
вони є способом двох різних речей.
Отже, якщо ви хочете знати, чи існує певний файл у дорозі, я вважаю за краще найкращий спосіб:
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)
}
}