問題描述
Windows 文件系統(tǒng)不區(qū)分大小寫.如何,給定文件/文件夾名稱(例如somefile"),我如何獲得該文件/文件夾的實際名稱(例如,如果資源管理器顯示它,它應(yīng)該返回SomeFile")?
Windows file system is case insensitive. How, given a file/folder name (e.g. "somefile"), I get the actual name of that file/folder (e.g. it should return "SomeFile" if Explorer displays it so)?
我所知道的一些方法,所有這些方法看起來都很落后:
Some ways I know, all of which seem quite backwards:
- 給定完整路徑,搜索路徑上的每個文件夾(通過 FindFirstFile).這給出了每個文件夾的正確大小寫結(jié)果.在最后一步,搜索文件本身.
- 從句柄中獲取文件名(如 MSDN 示例中所示)一>).這需要打開一個文件、創(chuàng)建文件映射、獲取它的名稱、解析設(shè)備名稱等.相當(dāng)復(fù)雜.它不適用于文件夾或零大小文件.
我是否遺漏了一些明顯的 WinAPI 調(diào)用?最簡單的,如 GetActualPathName() 或 GetFullPathName() 使用傳入的大小寫返回名稱(例如,如果傳入,則返回程序文件",即使它應(yīng)該是程序文件").
Am I missing some obvious WinAPI call? The simplest ones, like GetActualPathName() or GetFullPathName() return the name using casing that was passed in (e.g. returns "program files" if that was passed in, even if it should be "Program Files").
我正在尋找本機解決方案(不是 .NET 解決方案).
I'm looking for a native solution (not .NET one).
推薦答案
我在此回答我自己的問題,基于 來自 cspirz 的原始答案.
And hereby I answer my own question, based on original answer from cspirz.
這是一個給定絕對路徑、相對路徑或網(wǎng)絡(luò)路徑的函數(shù),它將返回大寫/小寫的路徑,就像它在 Windows 上顯示的那樣.如果路徑的某個組件不存在,它將返回從該點傳入的路徑.
Here's a function that given absolute, relative or network path, will return the path with upper/lower case as it would be displayed on Windows. If some component of the path does not exist, it will return the passed in path from that point.
它非常復(fù)雜,因為它試圖處理網(wǎng)絡(luò)路徑和其他邊緣情況.它對寬字符串進(jìn)行操作并使用 std::wstring.是的,理論上 Unicode TCHAR 可能與 wchar_t 不同;這是讀者的練習(xí):)
It is quite involved because it tries to handle network paths and other edge cases. It operates on wide character strings and uses std::wstring. Yes, in theory Unicode TCHAR could be not the same as wchar_t; that is an exercise for the reader :)
std::wstring GetActualPathName( const wchar_t* path )
{
// This is quite involved, but the meat is SHGetFileInfo
const wchar_t kSeparator = L'\';
// copy input string because we'll be temporary modifying it in place
size_t length = wcslen(path);
wchar_t buffer[MAX_PATH];
memcpy( buffer, path, (length+1) * sizeof(path[0]) );
size_t i = 0;
std::wstring result;
// for network paths (\servershareRestOfPath), getting the display
// name mangles it into unusable form (e.g. "\servershare" turns
// into "share on server (server)"). So detect this case and just skip
// up to two path components
if( length >= 2 && buffer[0] == kSeparator && buffer[1] == kSeparator )
{
int skippedCount = 0;
i = 2; // start after '\'
while( i < length && skippedCount < 2 )
{
if( buffer[i] == kSeparator )
++skippedCount;
++i;
}
result.append( buffer, i );
}
// for drive names, just add it uppercased
else if( length >= 2 && buffer[1] == L':' )
{
result += towupper(buffer[0]);
result += L':';
if( length >= 3 && buffer[2] == kSeparator )
{
result += kSeparator;
i = 3; // start after drive, colon and separator
}
else
{
i = 2; // start after drive and colon
}
}
size_t lastComponentStart = i;
bool addSeparator = false;
while( i < length )
{
// skip until path separator
while( i < length && buffer[i] != kSeparator )
++i;
if( addSeparator )
result += kSeparator;
// if we found path separator, get real filename of this
// last path name component
bool foundSeparator = (i < length);
buffer[i] = 0;
SHFILEINFOW info;
// nuke the path separator so that we get real name of current path component
info.szDisplayName[0] = 0;
if( SHGetFileInfoW( buffer, 0, &info, sizeof(info), SHGFI_DISPLAYNAME ) )
{
result += info.szDisplayName;
}
else
{
// most likely file does not exist.
// So just append original path name component.
result.append( buffer + lastComponentStart, i - lastComponentStart );
}
// restore path separator that we might have nuked before
if( foundSeparator )
buffer[i] = kSeparator;
++i;
lastComponentStart = i;
addSeparator = true;
}
return result;
}
再次感謝 cspirz 將我指向 SHGetFileInfo.
Again, thanks to cspirz for pointing me to SHGetFileInfo.
這篇關(guān)于在 Windows 上獲取實際文件名(帶有正確的大小寫)的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!