Когда можно проверить, существует ли файл? - PullRequest
35 голосов
/ 23 марта 2009

Файловые системы нестабильны. Это означает, что вы не можете доверять результату одной операции, чтобы он был действительным для следующей, даже если это следующая строка кода. Вы не можете просто сказать if (some file exists and I have permissions for it) open the file, и вы не можете сказать if (some file does not exist) create the file. Всегда существует вероятность того, что результат вашего условия if изменит между между двумя частями вашего кода. Операции различны: не атомарные.

Что еще хуже, характер проблемы означает, что если у вас возникнет искушение сделать эту проверку, скорее всего, вы уже обеспокоены или знаете, что что-то, что вы не контролируете, может произойти с файлом. Природа сред разработки делает это событие менее вероятным во время тестирования и очень трудным для воспроизведения. Так что не только у вас есть ошибка, но ошибка не будет отображаться во время тестирования.

Поэтому при нормальных обстоятельствах лучший способ - даже не пытаться проверить, существует ли файл или каталог. Вместо этого потратьте время на разработку исключений из файловой системы. В любом случае вы должны обработать эти исключения, так что это намного лучше использует ваши ресурсы. Несмотря на то, что исключения являются медленными, проверка существования файла требует дополнительной поездки на диск, и доступ к диску становится на намного медленнее. У меня даже есть хорошо проголосовавший ответ на этот счет в другом вопросе.

Но у меня есть некоторые сомнения. Например, в .Net, если это действительно всегда true, методы .Exists() не были бы в API в первую очередь. Также рассмотрите сценарии, в которых вы ожидаете, что вашей программе понадобится создать файл. Первый пример, который приходит на ум, касается настольного приложения. Это приложение устанавливает файл конфигурации пользователя по умолчанию в свой домашний каталог, и при первом запуске каждого пользователя приложение копирует этот файл в папку данных приложения этого пользователя. Ожидается, что файл не будет существовать при первом запуске.

Итак, когда допустимо заранее проверять наличие (или других атрибутов, таких как размер и разрешения) файла? Является ли ожидание неудачи, а не успеха с первой попытки достаточно хорошим эмпирическим правилом?

Ответы [ 18 ]

37 голосов
/ 23 марта 2009

Метод File.Exists существует главным образом для проверки существования файла, когда вы не собираетесь открывать файл. Например, проверка на наличие блокирующего файла, само существование которого говорит вам что-то, но чье содержимое несущественно.

Если вы собираетесь открыть файл, вам нужно будет обработать любое исключение независимо от результатов любых предыдущих вызовов File.Exists. Так что, в общем, в этих обстоятельствах нет смысла называть это. Просто используйте соответствующее значение перечисления FileMode в вашем методе open и обработайте любые исключения, как это просто.

РЕДАКТИРОВАТЬ: Несмотря на то, что это сформулировано в терминах .Net API, оно основано на базовом системном API. Как Windows, так и Unix имеют системные вызовы (т.е. CreateFile), которые используют эквивалент перечисления FileMode. Фактически в .Net (или Mono) значение FileMode просто передается системному вызову.

5 голосов
/ 13 января 2012

В качестве общей политики такие методы, как File.Exists или такие свойства, как WeakReference.Alive или SomeConcurrentQueue.Count, бесполезны в качестве средства обеспечения существования "хорошего" состояния, но могут быть полезны в качестве средства определения того, что «плохое» состояние существует без какой-либо ненужной (и, возможно, контрпродуктивной) работы. Такие ситуации могут возникать во многих сценариях, связанных с блокировками (и файлами, поскольку они часто включают в себя блокировки). Поскольку все подпрограммы, которым необходимо заблокировать набор ресурсов, должны, когда это практически возможно, всегда получать блокировки этих ресурсов в согласованном порядке, может потребоваться получить блокировку для одного ресурса, который, как ожидается, существует, до получения ресурса, который может или может не существовать. В таком случае, хотя невозможно избежать возможности того, что кто-то может заблокировать первый ресурс, не сможет получить второй, а затем снять первую блокировку, не выполнив с ней никакой полезной работы, проверив существование второго ресурса перед тем, как получение блокировки на первом минимизирует ненужные и бесполезные усилия.

4 голосов
/ 23 марта 2009

Это зависит от ваших требований, но один из способов - попытаться получить эксклюзивный дескриптор открытого файла с каким-то механизмом повторных попыток. Если у вас есть этот дескриптор, другому процессу будет трудно (или невозможно) удалить (или переместить) этот файл.

Я использовал код в .NET, похожий на следующий, чтобы получить эксклюзивный дескриптор файла, где я ожидаю, что какой-то другой процесс, возможно, будет записывать файл:

FileInfo fi = new FileInfo(fullFilePath);

int attempts = maxAttempts;
do
{
    try
    {
        // Asking to open for reading with exclusive access...
        fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.None);
    }
    // Ignore any errors... 
    catch {}

    if (fs != null)
    {
        break;
    }
    else
    {
        Thread.Sleep(100);
    }
}
while (--attempts > 0);
2 голосов
/ 23 марта 2009

Один пример: вы можете проверить наличие файлов, которые вы не можете открыть (например, из-за разрешений).

Другой, возможно, лучший пример: вы хотите проверить наличие файла устройства Unix. Но определенно не открывайте его; открытие его имеет побочные эффекты (например, открытие / закрытие /dev/st0 перемотает ленту)

1 голос
/ 23 марта 2009

Я проверю его только в том случае, если ожидаю, что он отсутствует (например, в настройках приложения), и только если мне нужно прочитать файл.

Если мне нужно записать файл, это либо файл журнала (так что я могу просто добавить его или создать новый), либо я заменяю его содержимое, так что я все равно могу его воссоздать.

Если я ожидаю, что файл существует, было бы правильно, если выдается исключение. Обработка исключений должна информировать пользователя или выполнять восстановление. Мое мнение таково, что это приводит к более чистому коду.

Защита файлов (т. Е. Не перезаписывание (возможно, важных) файлов) отличается, в этом случае я бы всегда проверял, существует ли файл, если инфраструктура не делает этого для меня (подумайте SaveFileDialog)

1 голос
/ 23 марта 2009

В среде * nix хорошо зарекомендовавший себя способ проверки, запущена ли еще одна копия программы, - это создать файл блокировки. Таким образом, проверка существования файла используется для проверки этого.

1 голос
/ 23 марта 2009

Я думаю, что проверка имеет смысл, когда вы хотите убедиться, что файл был там в первую очередь. Как вы сказали, файлы настроек ... если есть файл, я попытаюсь объединить существующие настройки вместо того, чтобы удалить их.

В других случаях пользователь говорит мне что-то сделать с файлом. Да, я знаю, openFileDialog проверит, существует ли файл (но это не обязательно). Я смутно помню, что в VB6 это было не так, поэтому проверка файла существовала, и они просто сказали мне, что это было обычным делом.

Я бы предпочел не программировать по исключению.

Редактировать

Я не упустил суть. Вы можете попытаться получить доступ к файлу, возникнет исключение, и затем, когда вы приступите к созданию файла, файл уже был помещен туда. Что теперь заставляет ваш код обработки исключений идти на фритюре. Поэтому, я думаю, мы могли бы иметь обработчик исключений в нашем обработчике исключений, чтобы понять, что файл снова изменился ...

Я бы лучше попытался предотвратить исключения, а не использовать их для управления логикой.

Редактировать

Кроме того, еще один раз, чтобы проверить атрибуты, такие как размер, - это когда вы ожидаете завершения файловой операции, да, вы никогда не знаете наверняка, но с хорошим алгоритмом и в зависимости от системы, записывающей файл, вы могли бы обработать много случаев (система работала в течение пяти лет, которая наблюдала за небольшими файлами, поступающими через ftp, и использовала тот же API-интерфейс, что и наблюдатель файловой системы, а затем начинает опрос, ожидая, пока файл перестанет изменяться, прежде чем вызывать событие что файл готов к использованию).

1 голос
/ 23 марта 2009

Это может быть слишком упрощенно, но я думаю, что основной причиной проверки существования файла (следовательно, существования .Exists ()) будет предотвращение непреднамеренной перезаписи существующих файлов, а не исключений, вызванных пытаясь получить доступ к несуществующим или недоступным файлам.

РЕДАКТИРОВАТЬ 2

Это было на самом деле слишком упрощенно, и я рекомендую вам посмотреть ответ Стивена Мартина.

0 голосов
/ 23 марта 2009

Я думаю, что в любое время, когда вы знаете, что файл может существовать или не существовать, и вы хотите выполнить какое-либо альтернативное действие, основанное на существовании файла, вы должны выполнить проверку, потому что в этом случае это не исключительное условие для файла не существовать. Это не освобождает вас от необходимости обрабатывать исключения - от чьего-либо удаления или создания файла между проверкой и открытием - но это проясняет цель программы и не полагается на обработку исключений для выполнения потока -контрольная логика.

РЕДАКТИРОВАТЬ : Примером может быть ротация журнала при запуске.

  try
  {
       if (File.Exists("app.log"))
       {
           RotateLogs();
       }

       log = File.Open("app.log", FileMode.CreateNew );
  }
  catch (IOException)
  {
     ...another writer, perhaps?
  }
  catch (UnauthorizedAccessException)
  {
     ...maybe I should have used runas?
  }
0 голосов
/ 08 мая 2009

Ваша проблема может быть легко решена с помощью базовых компьютерных наук ... читать дальше Семафоры .

(Я не хотел звучать как придурок, я просто указывал вам простой ответ на общую проблему).

...