Сообщений 30    Оценка 840 [+0/-5]         Оценить  
Система Orphus

Как определить, есть ли файл на диске?

11 способов как это сделать

Авторы: Игорь Ткачёв
Павел Блудов
Игорь Вартанов
Игорь Сухов
Александр Шаргин

Версия текста: 1.0.2

Способ 1 (access)

Библиотечная функция access позволяет определять режим доступа к файлу, а если второй параметр mode равен нулю, то определяется только существование файла.

        #include <io.h>
			bool FileExists(const char *fname)
			{
			  returnaccess(fname, 0) != -1;
			}
			

Хотя эта функция и не входит в стандарт C/C++, тем не менее, она присутствует в компиляторах Visual C++, Borland C++, Watcom C++ и многих других в неизменном виде.

Способ 2 (_findfirst)

Функция _findfirst возвращает информацию о первом файле, удовлетворяющем заданной маске поиска. Если указать точное имя файла, то мы сможем ответить на наш вопрос.

        #include <io.h>
			bool FileExists (const char *fname)
			{
			  _finddata_t data;
			  long nFind = _findfirst(fname,&data);
			  if (nFind != -1)
			  {
			    // Если этого не сделать, то произойдет утечка ресурсов
			    _findclose(nFind);
			    return true;
			  }
			  return false;
			}
			

С помощью этого способа можно определять не только существование отдельного файла, но также и группы файлов, соответствующей заданной маске. А если задать маску как "*.*", то можно узнать есть ли файлы в заданной директории.

Способ 3 (GetFileAttributes)

Функция GetFileAttributes Win32 API возвращает атрибуты для заданного файла или каталога. В случае ошибки возвращается значение 0xFFFFFFFF.

        #include <windows.h>
			bool FileExists(LPCTSTR fname)
			{
			  return::GetFileAttributes(fname) != DWORD(-1);
			}
			

Этот способ используется во многих примерах из MSDN, что позволяет предположить, что это штатный способ для решения нашей задачи в Win API. Кроме того, это самый быстрый из приведенных здесь способов.

Способ 4 (FindFirstFile)

Этот способ аналогичен способу 2 с той лишь разницей, что для достижения результата используется функция Win32 API.

        #include <windows.h>
			bool FileExists(LPCTSTR fname)
			{
			  WIN32_FIND_DATA wfd;
			  HANDLE hFind = ::FindFirstFile(fname, &wfd);
			  if (INVALID_HANDLE_VALUE != hFind)
			  {
			    // Если этого не сделать то произойдет утечка ресурсов
			    ::FindClose(hFind);
			    return true;
			  }
			  return false;
			}
			

Способ 5 (MFC)

MFC содержит класс-обёртку для функций Find... API. Мы вполне можем использовать этот класс.

        #include <afx.h>
			bool FileExists(LPCTSTR fname)
			{
			  returnCFileFind().FindFile(fname) == TRUE;
			}
			

Способ 6 (WTL)

Среди прочих классов, подобных MFC, WTL также содержит и CFindFile .Следовательно, этот способ внешне ни чем не отличается от предыдущего, кроме того, что не требует MFC.DLL. На самом деле этот способ намного быстрее предыдущего. Дело в том, что все функции класса CFindFile являются inline,так что код, генерируемый компилятором, почти целиком совпадает с кодом для способа 4.

        #define _WTL_NO_CSTRING // только для любителей "чистого" API#include <AtlMisc.h>
			bool FileExists(LPCTSTR fname)
			{
			  returnCFindFile().FindFile(fname) == TRUE;
			}
			

Способ 7 (PathFileExists)

Ещё один способ из предложенных Александром Шаргиным - использование SHLWAPI Path API .

        #include <windows.h>
			#include <shlwapi.h>
			#pragma comment(lib, "shlwapi")
			bool FileExists(LPCTSTR fname)
			{
			  return::PathFileExists(fname) == TRUE;
			}
			

Правда у этого способа имеются определённые недостатки, которые значительно сужают его практическое применение:

Способ 8 (CreateFile)

Самый очевидный и самый громоздкий способ.

        #include<windows.h>
			bool FileExists(LPCTSTR fname)
			{
			  HANDLE hFile = ::CreateFile(
			    fname, // file (or device) name0, // query access only
			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
			    NULL, // security attributes
			    OPEN_EXISTING, // disposition
			    FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN, // flags & attributes
			    NULL // template file
			    );
			  if (INVALID_HANDLE_VALUE != hFile)
			  {
			    ::CloseHandle(hFile);
			    return true;
			  }
			  return false;
			}
			

Способ 9 (Pure C++ метод std::ifstream, ::ifstream)

Данный метод состоит в создании временного объекта класса ifstream .Если файл с указанным именем не существует то operator void*() этого класса возвращает NULL pointer - иначе возвращается указатель на сам созданный объект (this). Это значение проверяется на NULL pointer - и ... все.

        #include <fstream>
			bool FileExists(const char *fname)
			{
			  returnstd::ifstream(fname) != NULL;
			}
			

... вернее почти все =)

В данном коде ifstream это typedef basic_ifstream < char, char_traits <char>> ifstream; если же Вы пользуетесь старыми заголовочными файлами (с расширением .h) - то для Вас ifstream - это никакой неtypedef- а самый настоящий класс. И все было бы прекрасно - если бы не одно но - в этом случае конструктор с именем файла в качестве параметра СОЗДАСТ файл (если он не существует) и в любом случае, проверка на существование файла даст положительный результат. Дело в том, что для "старого" ifstream 'а надо явно указывать что НЕ надо создавать файл через добавление флага ios::nocreate во втором параметре конструктора. А вот и сам код для такого случая:

        #include <fstream.h>
			bool FileExists(const char *fname)
			{
			  return  ::ifstream(fname, ios::in | ios::nocreate) != NULL;
			}
			

Данный метод хорош тем что он 100% портабелен - то есть используются только возможности самого языка С++ (в лице его стандартной библиотеки - которая является его частью).

Способ 10 (.NET)

Могу вас обрадовать, в .NET все наши мучения закончатся. Для выяснения существования файла можно будет просто вызвать метод FileExists класса File . Например:

System.IO.File.FileExists("c:\\autoexec.bat");

Способ 11 (Script)

Ни один из перечисленных способов не будет работать из .html документа. Зато из скрипта доступен Scripting.FileSystemObject и нам этого достаточно.

        function FileExists(fname)
			{
			  var fso = new ActiveXObject("Scripting.FileSystemObject");
			  returnfso.FileExists (fname);
			}
			

Мы вполне можем использовать Scripting.FileSystemObject и в COM-модуле:

			HRESULT FileExists(LPOLESTR oszFilename)
			{
			  CComPtr<IFileSystem> pfs;
			  HRESULT hr = pfs.CoCreateInstance(OLESTR("Scripting.FileSystemObject"));
			  if (SUCCEEDED(hr))
			  {
			    VARIANT_BOOL ret = VARIANT_FALSE;
			    hr = pfs->raw_FileExists(fname, &ret);
			    if (SUCCEEDED(hr))
			      hr = ret ? S_OK : S_FALSE;
			  }
			  return hr;
			}
			

Фактически, это очень извращенный способ вызова все той же функции access() из способа 1, с той разницей, что FileSystemObject работает с именами файлов в UNICODE и под WindowsNT/2k передает имя файла напрямую, а под Windows 9x/Me (и даже 3.1 с интернет эксплорером!) сам преобразовывает его в ANSI.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 30    Оценка 840 [+0/-5]         Оценить