【问题标题】:How to compare possibly alternative filenames in Windows?如何比较 Windows 中可能的替代文件名?
【发布时间】:2019-04-24 22:31:08
【问题描述】:

很遗憾,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)及更高版本。

EDIT(抱歉,第一次问得不好)

1) 用户输入的路径不保证指向一个实际存在的目录(当然,在这种情况下,它们不是同一个目录)。

2) 我知道文件和目录可以用非常不同的名称来引用,因为链接(硬链接或软链接)、substs、以不同名称或 IP 访问同一台计算机的网络等...但我并不是要检测所有这些情况。 我只想检查用户编写的名称是否是另一个现有名称的大小写变体(因此,例如,如果我尝试创建具有相同名称的文件,Windows 会告诉我该文件已经存在但情况不同)。

第二次编辑

这可以完成工作(至少在我尝试过的情况下):

if(CompareStringOrdinal(q,-1,data_from_CfileFind->txt.GetBuffer(),-1,1)==CSTR_EQUAL)

CompareStringOrdinal() 在旧版 Windows 中不可用。 有没有等价的?

【问题讨论】:

标签: c++ windows filenames


【解决方案1】:

最好的方法(据我所知)检查两个文件系统路径是否引用同一个项目,而不诉诸字符串比较,是:

  1. 使用CreateFile()打开HANDLEs 到两条路径,然后从HANDLEs 中获取唯一的文件系统ID,并比较它们是否相等。在 FAT 和 NTFS 上,使用 GetFileInformationByHandle() 中的 dwVolumeSerialNumbernFileIndex(Low|High) 的组合。在 ReFS 上,请改用 GetFileInformationByHandleEx(FileIdInfo) 中的 VolumeSerialNumberFileId 的组合。您可以使用GetVolumeInformation() 来检测每个路径正在使用哪个文件系统。

    BY_HANDLE_FILE_INFORMATIONFILE_ID_INFO 文档中分别描述了这种方法:

    标识符(低位和高位部分)和卷序列号唯一标识单台计算机上的文件。要确定两个打开的句柄是否代表同一个文件,请结合每个文件的标识符和卷序列号并进行比较。

    文件的 128 位文件标识符。文件标识符和卷序列号唯一标识单个计算机上的文件。要确定两个打开的句柄是否代表同一个文件,请结合每个文件的标识符和卷序列号并进行比较。

  2. 一种与文件系统无关的方法是使用SHGetDesktopFolder() 为Shell 命名空间的根桌面获取IShellFolder 接口,然后使用桌面的IShellFolder::ParseDisplayName() 方法(或使用独立的SHParseDisplayName()函数),然后使用桌面的IShellFolder::CompareIDs()方法比较PIDL。

【讨论】:

  • 恐怕我用这个问题误导了你(请参阅“编辑”)。但是,CreateFile() 不适用于某些目录(例如C:\System Volume Information - 但我无法创建例如C:\System VOLUME Information,因为它将是同一个目录......)。我会尝试方法 2. 或更简单的方法。非常感谢您的回答。
【解决方案2】:

首先在每个路径上调用GetFullPathName,然后调用GetLongPathName,最后对结果进行不区分大小写的比较。

GetFullPathName 将为每个文件提供一个完全限定的路径。 GetLongPathName 然后获取该路径的每个组件的真实名称,因此如果有人使用 Windows 95/98 样式的文件/目录短名称,这不会混淆。

【讨论】:

  • “最后对结果进行不区分大小写的比较”正是我的问题。我该如何执行?我尝试使用CompareString(LOCALE_INVARIANT,NORM_IGNORECASE ....,但它混淆了weißweiss 这是两个不同的目录。
【解决方案3】:

这就是它的工作方式:

1) 在程序开始时,调用setlocale(LC_CTYPE, "");

2) 然后将字符串与if(!data_from_CfileFind->txt.CompareNoCase(q)) 比较(即调用_wcsicmp 即调用_wcsicmp_l

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-10
    • 1970-01-01
    • 2021-10-31
    • 1970-01-01
    • 2015-07-06
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    相关资源
    最近更新 更多