2013-08-19 25 views
13

目前我使用此代碼來檢查文件是否在WindowsPOSIX兼容的操作系統(Linux操作系統,Android的,MacOS的,的iOS,黑莓10)存在:如何檢查文件是否以便攜方式存在於C++中?

bool FileExist(const std::string& Name) 
{ 
#ifdef OS_WINDOWS 
    struct _stat buf; 
    int Result = _stat(Name.c_str(), &buf); 
#else 
    struct stat buf; 
    int Result = stat(Name.c_str(), &buf); 
#endif 
    return Result == 0; 
} 

問題:

  1. 這段代碼是否有任何缺陷? (也許是一個操作系統,它不能被編譯)

  2. 是否有可能以真正便攜的方式使用C/C++標準庫來實現它?

  3. 如何改進?尋找典型的例子。

+1

這是檢查,如果它存在的目的,例如你打算打開文件,如果它存在,或打印錯誤消息,或其他? –

+4

它應該很好。我會專門檢查Windows和POSIX,默認情況下是POSIX類似的東西。您應該也可以定義一個項目特定的操作系統,因爲這些名稱本身可能會隨系統而變化。 – Jiminion

+0

@MatsPetersson:打印錯誤消息是其中一個用例。 –

回答

20

因爲C++也被標記了,我會用boost::filesystem

#include <boost/filesystem.hpp> 

bool FileExist(const std::string& Name) 
{ 
    return boost::filesystem::exists(Name); 
} 

場景

顯然,提升使用stat對POSIX和DWORD attr(::GetFileAttributesW(FileName));在Windows(注背後:我這裏提取了代碼的相關部分,可能是我做錯了,但應該是這樣)。

基本上,除了返回值,boost會檢查errno值以檢查文件是否真的不存在,或者您的stat因爲其他原因失敗。

#ifdef BOOST_POSIX_API 

struct stat path_stat; 
if (::stat(p.c_str(), &path_stat)!= 0) 
{ 
    if (ec != 0)       // always report errno, even though some 
    ec->assign(errno, system_category()); // errno values are not status_errors 

    if (not_found_error(errno)) 
    { 
    return fs::file_status(fs::file_not_found, fs::no_perms); 
    } 
    if (ec == 0) 
    BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status", 
     p, error_code(errno, system_category()))); 
    return fs::file_status(fs::status_error); 
} 

#else 
    DWORD attr(::GetFileAttributesW(p.c_str())); 
    if (attr == 0xFFFFFFFF) 
    { 
     int errval(::GetLastError()); 
     if (not_found_error(errval)) 
     { 
      return fs::file_status(fs::file_not_found, fs::no_perms); 
     } 
    } 
#endif 

not_found_error爲Windows和POSIX分別定義:

的Windows:

bool not_found_error(int errval) 
    { 
    return errval == ERROR_FILE_NOT_FOUND 
     || errval == ERROR_PATH_NOT_FOUND 
     || errval == ERROR_INVALID_NAME // "tools/jam/src/:sys:stat.h", "//foo" 
     || errval == ERROR_INVALID_DRIVE // USB card reader with no card inserted 
     || errval == ERROR_NOT_READY // CD/DVD drive with no disc inserted 
     || errval == ERROR_INVALID_PARAMETER // ":sys:stat.h" 
     || errval == ERROR_BAD_PATHNAME // "//nosuch" on Win64 
     || errval == ERROR_BAD_NETPATH; // "//nosuch" on Win32 
    } 

POSIX:

bool not_found_error(int errval) 
    { 
    return errno == ENOENT || errno == ENOTDIR; 
    } 
+7

+1提升。但是僅對這個函數使用Boost(我們不使用Boost來做其他任何事情)有點貪婪。 –

+1

也許你可以通過解釋'boost :: filesystem :: exists'裏面的內容來擴展你的答案嗎?這可能是一個很好的答案。 –

+1

@SergeyK。會做,謝謝。 –

3

我perosnally喜歡只是試圖打開該文件:

bool FileExist(const std::string& Name) 
{ 
    std::ifstream f(name.c_str()); // New enough C++ library will accept just name 
    return f.is_open(); 
} 

應該處理任何有文件的東西[C++標準不要求],因爲它使用C++ std::string,我不明白爲什麼std::ifstream應該是一個問題。

+8

打開文件是一個壞主意 - 它可以存在,但可以通過另一個進程打開以不共享的方式。 –

+2

我不確定是否有任何方法可以100%防止錯誤 - 該文件現在也可能存在,並在下次運行此進程時被刪除。或者它可能屬於不同的用戶,所以我們無權打開(或「統計」等)。任何「文件存在」方法都是最好的建議。如果知道它是否存在是爲了「避免將文件保存在現有文件上」,那麼無法打開該文件並不是問題,因爲您也無法打開該文件來寫一行或三個以後[放在一邊當然,從種族的角度來看,通過任何方法都可以實現。 –

+1

但問題是如何改善現有的代碼。你的代碼刪除了''#ifdef'',但增加了另一個假設。這不是一種改進,而是一種折衷。 –

1
  1. 此代碼是否有任何缺陷? (也許一個OS它不能被編譯)

Result == 0 「跳過」 ENAMETOOLONGELOOP,錯誤等按照this

我能想到的是:ENAMETOOLONG路徑太長如下: -

在很多情況下,在遞歸掃描期間,子文件夾/目錄不斷增加,如果路徑太長,可能會導致此錯誤,但文件仍然存在!

類似的情況也可能發生在其他錯誤。

此外,

作爲每this, I'ld喜歡使用重載boost::filesystem::exists方法

bool exists(const path& p, system::error_code& ec) noexcept;

+1

對於一個陷阱+1。但如何克服這一點? –

+1

@SergeyK。不知道,可能是我們可以嘗試每次重複更改目錄,然後開始掃描。 – P0W

+1

@SergeyK。看看增強是如何實現的 - 基本上 - 除了返回值,你需要檢查最後的錯誤代碼集。 –