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

ОГЛАВЛЕНИЕ

Способ 1 (access)

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


#include <io.h>
 
bool FileExists (const char *fname)
{
    return access(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)
{
    return CFileFind().FindFile(fname) == TRUE;
}


 

Способ 6 (WTL)

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


#define _WTL_NO_CSTRING // только для любителей "чистого" API
#include <AtlMisc.h>
 
bool FileExists (LPCTSTR fname)
{
    return CFileFind().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;
}

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

§                     Он не будет работать, если на компьютере не установлен Internet Explorer 4.0 или выше, что может быть вполне вероятно на компьютерах с ранними версиями Windows 95 и Window NT 4.0.

§                     Функция PathFileExists() не поддерживает UNC имена файлов.


 

Способ 8 (CreateFile)

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


#include <windows.h>
 
bool FileExists (LPCTSTR fname)
{
    HANDLE hFile = ::CreateFile(
        fname,                                              // file (or device) name
        0,                                                  // 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)
{
    return std::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");
     return fso.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.