Як перевірити, чи існує файл у Go?


436

Стандартна бібліотека Go не має функції, призначеної виключно для перевірки існування файлу чи ні (як Python os.path.exists). Який ідіоматичний спосіб це зробити?


Я насправді не розумію. У ту ж хвилину ти кажеш, що немає стандартної функції, і ти пишеш відповідь зі стандартною функцією. Що я пропускаю? Не варто принаймні запитання виправляти?
Denys Séguret

@dystroy - вирішив питання.
Шрідхар Ратнакумар

11
Слід уникати запитів про існування файлів. Враховуючи раціональний характер відповіді, отримана інформація говорить, що насправді нічого корисного не було, ніж файл, який існував у запитаний час, - але він більше не може існувати. Рекомендований спосіб - просто відкрити файл і перевірити, чи це не вдається чи ні.
zzzz

2
На це вже відповіли тут
Сергій Куліков

2
@zzzz (я знаю, минули роки, цей коментар - для нових читачів) Я згоден у загальному випадку. Але моя програма завантажує бібліотеку сторонніх розробників, яка приймає деякий шлях до файлу як дані ініціалізації, але segfault, якщо файл не існує. Я думаю, що це правильний сценарій для перевірки наявності файлу без спроби відкрити його, щоб мати можливість повідомляти про помилку без фатальної аварії, оскільки мій код не потребує читання вмісту файлу або запису в файл безпосередньо.
Серхіо Акоста

Відповіді:


693

Щоб перевірити, чи файл не існує, еквівалентний файлу 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

43
Другий фрагмент більш тонко неправильний; умова повинна бути !os.IsNotExist(err). Можливо, файл існує, але os.Statне працює з інших причин (наприклад, дозвіл, збій диска). Використовуючи err == nilяк умову, неправильно класифікує такі збої як "файл не існує".
sqweek

9
Перевірити, чи файл існує неправильно: помилка є
нульовою,

1
Переконайтеся, що розширити ~ інакше воно поверне помилкове ... stackoverflow.com/questions/17609732/…
Marcello de Sales

Ви можете використовувати os.IsExist () залежно від конкретного випадку, інакше може бути замість того, щоб робити подвійне заперечення під час виконання! Os.IsNotExistant ()
Аріель Монако

126

Відповідь 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


31

Ви повинні використовувати функції 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
}

Приклад витягнутий звідси .


12
Остерігайтеся: як вказувало stackoverflow.com/a/22467409/712014 , цей код повертає істину, навіть якщо файл не існує, наприклад, коли Stat () повернення дозволу відмовлено.
Майкл

19

Приклад по 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
}

7
Що таке приклад 5? Чи можете ви бути конкретними, будь ласка.
xlm

1
У вашому другому прикладі потрібно знищити кілька повернених значень - наприклад, _, err: = os.Stat (ім'я)
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)
    }


6

Розглянемо спочатку декілька аспектів, обидва функції, що надаються 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
} 

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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.