我有以下适用于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上相同的方式工作?
我已经看到了各种StackOverflow的答案,这些答案部分回答了我的问题,但我把它们放在一起有问题。我的系统依赖于宽字符串,尤其是在Windows上,文件名可能包含非ASCII字符。我知道通常最好写入文件并检查错误,但我的情况恰恰相反 - 如果文件已存在,我需要跳过该文件。我只想检查文件是否存在,无论我是否可以读/写。
答案 0 :(得分:1)
在FAT和NTFS以外的许多文件系统上,文件名不能很好地定义为字符串。它们在技术上是字节序列。这些字节序列的含义是一个解释问题。常见的解释是类似UTF-8。不完全是UTF-8,因为Unicode指定字符串相等而不管编码。大多数系统使用字节相等。 (同样,FAT和NTFS是例外,使用不区分大小写的比较)
答案 1 :(得分: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
}
上面的函数使用最快的方法来检查每个操作系统变体的文件,并且如果您使用的是除了明确列出的操作系统之外的其他操作系统(例如原始的Amiga OS),则会有后备。这已在GCC4.8.x和VS 2010/2012中使用。
good方法将检查所有内容是否正常,这样您实际上打开了文件。
唯一需要注意的是如何在操作系统中表示文件名(如另一个答案中所述)。
到目前为止,这对我来说已经过了很好的交叉平台:)
答案 2 :(得分: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
来定义我自己的字符串版本,在Windows上为wstring
,在* nix上为string
因为* nix似乎对UTF8符号更加自由,并且不介意在纯字符串中使用它们。尽管如此,我仍希望保持我的函数声明对所有平台都一致,同时我也想了解Unicode文件名在Linux中是如何工作的。