Windows文件系统不区分大小写。 如何,给定一个文件/文件夹名称(例如“somefile”),我得到该文件/文件夹的实际名称(例如,如果资源pipe理器显示它应该返回“SomeFile”)?
我知道的一些方式,所有这些看起来都很倒退:
我是否缺less一些明显的WinAPI调用? 最简单的,如GetActualPathName()或GetFullPathName()返回传入的名字(例如,如果传入的是程序文件,即使它应该是“Program Files”也会返回)。
我正在寻找一个本地解决scheme(不是.NET的)。
在此我根据cspirz的原始回答回答我自己的问题。
这是一个给定绝对,相对或网络路径的函数,将返回大小写的路径,就像在Windows上显示的一样。 如果路径的某个组件不存在,它将从该点返回传入的路径。
这是相当涉及,因为它试图处理网络路径和其他边缘情况。 它使用宽字符字符串并使用std :: wstring。 是的,从理论上讲,Unicode TCHAR可能与wchar_t不一样; 这是一个读者的练习:)
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 (\\server\share\RestOfPath), getting the display // name mangles it into unusable form (eg "\\server\share" 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。
还有另一个解决方案。 首先调用GetShortPathName(),然后GetLongPathName()。 猜猜会使用什么字符的情况呢? 😉
你有没有尝试过使用SHGetFileInfo?
好吧,这是VBScript,但即使如此,我建议使用Scripting.FileSystemObject对象
Dim fso Set fso = CreateObject("Scripting.FileSystemObject") Dim f Set f = fso.GetFile("C:\testfile.dat") 'actually named "testFILE.dAt" wscript.echo f.Name
我得到的答复是从这个片段是
testFILE.dAt
希望至少能让你朝正确的方向发展。
编辑:现在我重读这个问题,看来OP是知道这个解决方案,并寻求其他解决方案。
我发现FindFirstFile()
将返回fd.cFileName
正确的套管文件名(路径的最后部分)。 如果我们将c:\winDOWs\exPLORER.exe
作为第一个参数传递给FindFirstFile()
,则fd.cFileName
将是explorer.exe
如下所示:
如果我们用fd.cFileName
替换路径的最后一部分,我们将得到最后的部分; 路径将变成c:\winDOWs\explorer.exe
。
假设路径总是绝对路径(文本长度没有变化),我们可以将这个“算法”应用到路径的每个部分(驱动器字母部分除外)。
说话很便宜,这里是代码:
#include <windows.h> #include <stdio.h> /* c:\windows\windowsupdate.log --> c:\windows\WindowsUpdate.log */ static HRESULT MyProcessLastPart(LPTSTR szPath) { HRESULT hr = 0; HANDLE hFind = NULL; WIN32_FIND_DATA fd = {0}; TCHAR *p = NULL, *q = NULL; /* thePart = GetCorrectCasingFileName(thePath); */ hFind = FindFirstFile(szPath, &fd); if (hFind == INVALID_HANDLE_VALUE) { hr = HRESULT_FROM_WIN32(GetLastError()); hFind = NULL; goto eof; } /* thePath = thePath.ReplaceLast(thePart); */ for (p = szPath; *p; ++p); for (q = fd.cFileName; *q; ++q, --p); for (q = fd.cFileName; *p = *q; ++p, ++q); eof: if (hFind) { FindClose(hFind); } return hr; } /* Important! 'szPath' should be absolute path only. MUST NOT SPECIFY relative path or UNC or short file name. */ EXTERN_C HRESULT __stdcall CorrectPathCasing( LPTSTR szPath) { HRESULT hr = 0; TCHAR *p = NULL; if (GetFileAttributes(szPath) == -1) { hr = HRESULT_FROM_WIN32(GetLastError()); goto eof; } for (p = szPath; *p; ++p) { if (*p == '\\' || *p == '/') { TCHAR slashChar = *p; if (p[-1] == ':') /* p[-2] is drive letter */ { p[-2] = toupper(p[-2]); continue; } *p = '\0'; hr = MyProcessLastPart(szPath); *p = slashChar; if (FAILED(hr)) goto eof; } } hr = MyProcessLastPart(szPath); eof: return hr; } int main() { TCHAR szPath[] = TEXT("c:\\windows\\EXPLORER.exe"); HRESULT hr = CorrectPathCasing(szPath); if (SUCCEEDED(hr)) { MessageBox(NULL, szPath, TEXT("Test"), MB_ICONINFORMATION); } return 0; }
优点:
FindFirstFile()
非常快,直接的缓冲区操作使其更快。 缺点:
代码风格背后的原因:
我使用goto
进行错误处理,因为我已经习惯了(在C中goto
对于错误处理非常方便)。 我使用for
循环来执行像strcpy
和strchr
功能,因为我想确定实际执行的是什么。
FindFirstFileNameW将有一些缺点:
据我所知,System.IO.FileInfo类Name属性将从Windows返回你的实际名称。
经过一个快速的测试, GetLongPathName()做你想要的。
这是诀窍:
win32file.FindFilesW('somefile')[0][-2]
返回“SomeFile”。
编辑:愚蠢的我,我在Python中寻找相同的。 所以忽略这个C / C ++ …