不幸的是,Windows中的目录/文件名不区分大小写。
当我将文本(来自用户的输入)与目录名(来自CFileFind
)进行比较时,如何检查它们是否表示相同的目录?例如C:\PIPPO\
和C:\Pippo\
是同一目录,而C:\Pippò\
则不一样(最后一个有重音)。
我正在尝试:
if(CompareString(LOCALE_INVARIANT,NORM_IGNORECASE,q,-1,data_from_CfileFind->txt.GetBuffer(),-1)==CSTR_EQUAL)
(q
是[用户输入的一部分])
它“有点工作”,因为它识别罗马,希腊和西里尔字母的案例变体的相同目录,但它混淆"weiß"
和"weiss"
(它们是我的光盘上的两个不同的目录),所以它不可靠。
[失败的测试受到Comparing and sorting Unicode filenames的启发:我已经阅读过,但没有找到合适的解决方案 - 链接似乎不起作用)
(我也读过Windows Invariant Culture Puzzle,但我担心我对“文化”并不完全了解)。
有什么建议吗?
也许我应该用不同的参数调用CompareString()
?或者有更好,更简单的方法吗?
请注意,我不需要对名称进行排序:我只想检查它们是否与Windows相同的目录(或文件)。
“Windows”,我的意思是从2000年(或至少XP)以后。
编辑(对不起,第一次问题不是很好)
1)用户输入的路径不保证引用实际存在的目录(在这种情况下,当然,它们不是同一目录)。
2)我知道文件和目录可以用非常不同的名称来引用,因为链接(硬或软),subst
s,具有不同名称或IP的网络访问到同一台计算机等等......但我不是问检测所有这些案件。我要检查的是,用户编写的名称是否是另一个现有名称的大小写(因此,例如,如果我尝试创建一个具有相同名称的文件,Windows会告诉我该文件已经存在但不同的情况)。
第二次编辑
这样做(至少在我尝试过的情况下):
if(CompareStringOrdinal(q,-1,data_from_CfileFind->txt.GetBuffer(),-1,1)==CSTR_EQUAL)
但CompareStringOrdinal()
在较旧的Windows版本中不可用。有没有相应的?
最好的方法(我知道)检查两个文件系统路径是否引用相同的项目,而不采用字符串比较,是:
HANDLE
将CreateFile()
s打开到两个路径,然后从HANDLE
s获取唯一的文件系统ID并将它们进行相等性比较。在FAT和NTFS上,使用来自dwVolumeSerialNumber
的nFileIndex(Low|High)
和GetFileInformationByHandle()
的组合。在ReFS上,使用来自VolumeSerialNumber
的FileId
和GetFileInformationByHandleEx(FileIdInfo)
的组合。您可以使用GetVolumeInformation()
来检测每个路径正在使用哪个文件系统。
这种方法分别在BY_HANDLE_FILE_INFORMATION
和FILE_ID_INFO
文档中描述:
标识符(低和高部分)和卷序列号唯一地标识单个计算机上的文件。要确定两个打开的句柄是否表示同一文件,请合并每个文件的标识符和卷序列号并进行比较。
文件的128位文件标识符。文件标识符和卷序列号唯一标识单个计算机上的文件。要确定两个打开的句柄是否表示同一文件,请合并每个文件的标识符和卷序列号并进行比较。SHGetDesktopFolder()
获取Shell命名空间的根桌面的IShellFolder
接口,然后使用桌面的IShellFolder::ParseDisplayName()
方法(或使用独立的SHParseDisplayName()
函数)解析两个路径到绝对PIDL,然后使用qzxswpoi比较PIDL桌面的IShellFolder::CompareIDs()
方法。首先在每条路径上调用GetFullPathName,然后执行GetLongPathName,最后对结果进行不区分大小写的比较。
GetFullPathName
将为每个文件提供完全限定的路径。然后GetLongPathName
获取该路径的每个组件的真实名称,因此如果有人使用Windows 95/98样式的短名称作为文件/目录,那就不会混淆了。
这就是它的工作方式:
1)在程序开始时,调用setlocale(LC_CTYPE, "");
2)然后将字符串与if(!data_from_CfileFind->txt.CompareNoCase(q))
(调用_wcsicmp
调用_wcsicmp_l
)进行比较