C++ 如何在创建文件之前检查文件是否已存在?(C+;+;,Unicode,跨平台)

C++ 如何在创建文件之前检查文件是否已存在?(C+;+;,Unicode,跨平台),c++,unicode,filesystems,cross-platform,file-exists,C++,Unicode,Filesystems,Cross Platform,File Exists,我有以下适用于Windows的代码: 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 } 考虑到scr

我有以下适用于Windows的代码:

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
}
考虑到
scr
是一个Unicode字符串,可能包含带有Unicode字符的文件路径,我如何使代码在*nix平台上与在Windows上的工作方式相同


我看到了各种各样的答案,它们部分地回答了我的问题,但我很难把它们放在一起。我的系统依赖于宽字符串,尤其是在文件名可能包含非ASCII字符的Windows上。我知道一般情况下最好写入文件并检查错误,但我的情况正好相反——如果文件已经存在,我需要跳过它。我只想检查文件是否存在,不管我是否能读/写它。

在FAT和NTFS以外的许多文件系统上,文件名并没有准确地定义为字符串。从技术上讲,它们是字节序列。这些字节序列的含义是一个解释问题。一种常见的解释是类似UTF-8的。不完全是UTF-8,因为不管编码如何,Unicode都指定字符串相等。大多数系统使用字节相等。(同样,FAT和NTFS是例外,使用不区分大小写的比较)

我使用的一个好的便携式解决方案是使用以下内容:

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):

上面的函数使用最快的方法检查每个操作系统变体的文件,并且在您使用的操作系统不是明确列出的操作系统(例如,原始的Amiga操作系统)时具有回退功能。这已在GCC4.8.x和VS 2010/2012中使用

该方法将检查所有内容是否正常,这样您就可以实际打开文件

唯一需要注意的是,请密切注意文件名在操作系统中的表示方式(如另一个答案中所述)


到目前为止,这对我来说在跨平台上运行得很好:)

我花了几个小时在我的Ubuntu机器上进行实验。它经历了多次尝试和错误,但最终我还是成功了。我不确定它是否能在MacOS甚至其他*尼克斯上运行

正如许多人怀疑的那样,直接转换到
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
//这对于在控制台中输出正确的unicode字符以及wcstombs的工作至关重要!
//空字符串也可以代替en_US.UTF-8
//setlocale(LC_ALL,“en_US.UTF-8”);
wstring UNICODE=wstring(L)/主/程序/ббббббббббббб;
int outSize=unicoded.size()*sizeof(wchar\u t)+1;//最大可能字节数加上\0个字符
char*conv=新字符[超大];
memset(conv,0,特大型);
size_t szt=wcstombs(conv,unicoded.c_str(),特大号);//这需要setlocale—只有在返回31时才需要。否则它会返回一些大的数字——很可能是一条错误消息

wcout
if(std::ifstream(name))
?对于linux案例,请参阅:@Heather-这不会自动尝试打开文件吗?如果我没有对现有文件的写入或读取权限,该怎么办?在这种情况下,我不希望
fileExists
返回false。@TimDave-不幸的是,这不能解释我应该如何处理wstring
access()
想要
char*
但我只能从wstring中获得
wchar\u t*
。在*nix平台上将
wstring::c_str()
转换为
char*
安全吗?@Martin那么您的问题是关于如何将wstring转换为char*?谢谢您的代码。不幸的是,我无法使用它有两个问题:1)我想知道该文件存在,即使我没有打开它的权限-
fopen
对我不起作用,2)如果文件名包含Unicode字符,例如,āēīāāāāāāāāāāāā。我生活在一个我们习惯于使用来自不同ANSI代码页的符号的拉脱维亚和俄罗斯文件名的国家,因此只有Windows Unicode API能够正确处理它们。啊,我现在明白了,把它留给我吧,今晚我将在使Unicode更加敏感方面大展拳脚:)这太棒了。它真的帮助了我。谢谢
// 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