2014-01-15 28 views
1

我有以下的代碼在Windows上運行:如何在創建文件之前檢查文件是否已經存在? (C++,統一的,跨平臺)

bool fileExists(const wstring& src) 
{ 
#ifdef PLATFORM_WINDOWS 
     return (_waccess(src.c_str(), 0) == 0); 
#else 
     // ???? how to make C access() function to accept the wstring on Unix/Linux/MacOS ? 
#endif 
} 

我如何在* nix平臺的代碼的工作,因爲它確實在Windows上以同樣的方式,考慮到scr是一個Unicode字符串,可能包含Unicode字符的文件路徑?

我見過各種StackOverflow答案,這部分回答了我的問題,但我有問題把它放在一起。我的系統依賴於寬字符串,尤其是在文件名可能包含非ASCII字符的Windows上。我知道通常最好寫入文件並檢查錯誤,但我的情況正好相反 - 如果文件已經存在,我需要跳過該文件。我只想檢查文件是否存在,無論我是否可以讀/寫它。

+2

'if(std :: ifstream(name))'? – Cynede

+0

對於Linux的情況下,請參閱:http://stackoverflow.com/questions/15470639/how-to-check-if-a-file-exists-in-c – TimDave

+0

@ Heather - 不會自動嘗試打開文件?如果我沒有對現有文件的寫入或讀取權限 - 我不希望'fileExists'在這種情況下返回false。 – JustAMartin

回答

1

在FAT和NTFS以外的許多文件系統上,文件名並不完全定義爲字符串。它們在技術上是字節序列。那些字節序列意味着什麼是解釋的問題。一個常見的解釋是UTF-8。不是確切的UTF-8,因爲Unicode指定字符串相等而不管編碼。大多數系統使用字節相等。 (同樣,FAT和NTFS有例外,使用不區分大小寫的比較)

1

我使用一個很好的便攜式解決方案是使用以下內容:

ifstream my_file(myFilenameHere); 
if (my_file.good()) 
{ 
    // file exists and do what you need to do when it exists 
} 
else 
{ 
    // the file doesn't exist do what you need to do to create it etc. 
} 

例如小型文件存在檢驗器功能可以是(這在Windows,Linux和UNIX)作品之一:

inline bool doesMyFileExist (const std::string& myFilename) 
{ 
#if defined(__unix__) || defined(__posix__) || defined(__linux__) 
// all UNIXes, POSIX (including OS X I think (cant remember been a while)) and 
// all the various flavours of Linus Torvalds digital offspring:) 

    struct stat buffer; 
    return (stat (myFilename.c_str(), &buffer) == 0); 

#elif defined(__APPLE__)|| defined(_WIN32) 
// this includes IOS AND OSX and Windows (x64 and x86) 
// note the underscore in the windows define, without it can cause problems 
    if (FILE *file = fopen(myFilename.c_str(), "r")) 
    { 
     fclose(file); 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
#else // a catch-all fallback, this is the slowest method, but works on them all:) 
    ifstream myFile(myFilename.c_str()); 
    if (myFile.good()) 
    { 
     myFile.close(); 
     return true; 
    } 
    else 
    { 
     myFile.close(); 
     return false; 
    } 
#endif 
} 

上面的函數使用最快的方法來檢查每個OS變種的文件,並有一個後備的情況下,你是不是那些明確以外的OS列出(例如原始的Amiga OS)。這已在GCC4.8.x和VS 2010/2012中使用。

good方法將檢查一切是否如實,並且這樣您實際上已打開文件。

唯一需要注意的是如何在操作系統中表示文件名(如另一個答案中所述)。

到目前爲止,這工作的跨平臺對我來說就好:)

+0

謝謝你的代碼。不幸的是,它有兩個問題,爲什麼我不能使用它:1)即使我沒有權限打開文件,我想知道文件存在 - 'fopen'不適用於我,2)它可能無法在Windows上運行if文件名包含Unicode字符,例如,āēīабвгд。我住在一個我們習慣了拉脫維亞語和俄語文件名的國家,這些國家的符號來自不同的ANSI代碼頁,因此只有Windows Unicode API才能正確處理它們。 – JustAMartin

+0

啊,我現在看到了,把它留給我,我今晚會有更多的unicode敏感:) – GMasucci

0

我花了一些時間嘗試我的Ubuntu的機器上。它花了很多試驗和錯誤,但最終我得到了它的工作。我不確定它是否可以在MacOS或其他* nixes上運行。

許多人懷疑,直接投到char*沒有工作 - 然後我只得到我的測試路徑/home/progmars/абвгдāēī的第一個斜線。訣竅是使用wcstombs()setlocale()相結合儘管在轉換後我無法在控制檯上顯示文本,但仍然使用access()函數。

這裏是爲我工作的代碼:

bool fileExists(const wstring& src) 
{ 
#ifdef PLATFORM_WINDOWS 

    return (_waccess(src.c_str(), 0) == 0); 

#else 
    // hopefully this will work on most *nixes... 

    size_t outSize = src.size() * sizeof(wchar_t) + 1;// max possible bytes plus \0 char 
    char* conv = new char[outSize]; 
    memset(conv, 0, outSize); 

    // MacOS claims to have wcstombs_l which has locale argument, 
    // but I could not find something similar on Ubuntu 
    // thus I had to use setlocale(); 
    char* oldLocale = setlocale(LC_ALL, NULL); 
    setlocale(LC_ALL, "en_US.UTF-8"); // let's hope, most machines will have "en_US.UTF-8" available 
    // "Works on my machine", that is, Ubuntu 12.04 

    size_t wcsSize = wcstombs(conv, src.c_str(), outSize); 
    // we might get an error code (size_t-1) in wcsSize, ignoring for now 

    // now be good, restore the locale 
    setlocale(LC_ALL, oldLocale); 

    return (access(conv, 0) == 0); 

#endif 
} 

這裏是一些實驗性的代碼,使我的解決方案:

// this is crucial to output correct unicode characters in console and for wcstombs to work! 
// empty string also works instead of en_US.UTF-8 
// setlocale(LC_ALL, "en_US.UTF-8"); 

wstring unicoded = wstring(L"/home/progmars/абвгдāēī"); 
int outSize = unicoded.size() * sizeof(wchar_t) + 1;// max possible bytes plus \0 char 
char* conv = new char[outSize]; 
memset(conv, 0, outSize); 
size_t szt = wcstombs(conv, unicoded.c_str(), outSize); // this needs setlocale - only then it returns 31. else it returns some big number - most likely, an error message 
wcout << "wcstombs result " << szt << endl; 

int resDirect = access("/home/progmars/абвгдāēī", 0); // works fine always 
int resCast = access((char*)unicoded.c_str(), 0); 
int resConv = access(conv, 0); 

wcout << "Raw " << unicoded.c_str() << endl; // output /home/progmars/абвгдāēī but only if setlocale has been called; else output is /home/progmars/???????? 
wcout << "Casted " << (char*)unicoded.c_str() << endl; // output/  
wcout << "Converted " << conv << endl; // output /home/progmars/ - for some reason, Unicode chars are cut away in the console, but still they are there because access() picks them up correctly 

wcout << "resDirect " << resDirect << endl; // gives correct result depending on the file existence 
wcout << "resCast " << resCast << endl; // wrong result - always 0 because it looks for/and it's the filesystem root which always exists  
wcout << "resConv " << resConv << endl; 
// gives correct result but only if setlocale() is present 

當然,我能避免所有的麻煩ifdef s來定義我自己的字符串版本,在Windows上是wstring,在* nix上是string,因爲* nix似乎對UTF8符號更加自由,不介意在純字符串中使用它們。儘管如此,我仍然希望我的函數聲明能夠在所有平臺上保持一致,並且我想了解Unicode文件名在Linux中的工作原理。

相關問題