C++ Windows上带有通配符的目录中的文件
如何从包含通配符的路径轻松获取所有文件路径?例如:C:/Data*Set/Files*/*.txt,我在Linux上使用C++ Windows上带有通配符的目录中的文件,c++,winapi,C++,Winapi,如何从包含通配符的路径轻松获取所有文件路径?例如:C:/Data*Set/Files*/*.txt,我在Linux上使用glob函数编写了它,但在Windows上无法做到: FindFirstFile不幸的是,它不支持目录名中的通配符 我认为应该有一个可用的Windows解决方案,但我找不到它。因此,您应该放弃使用特定于操作系统的文件访问,转而使用独立于操作系统的: 假设您得到了filesystem::path input,其中包含带有通配符的路径。要使用此解决您的问题,您需要: 用于将输入拆分
glob
函数编写了它,但在Windows上无法做到:
FindFirstFile
不幸的是,它不支持目录名中的通配符
我认为应该有一个可用的Windows解决方案,但我找不到它。因此,您应该放弃使用特定于操作系统的文件访问,转而使用独立于操作系统的: 假设您得到了
filesystem::path input
,其中包含带有通配符的路径。要使用此解决您的问题,您需要:
输入拆分为目录
输入
文件名输入开始的相对或绝对路径获取
begin
和end
迭代器引入到获得的父路径、目录迭代器和文件名中“*”
时,请使用带有迭代器的正则表达式来确定应前进到下一个目录
路径
regex GenerateRegex(string& arg) {
for (auto i = arg.find('*'); i != string::npos; i = arg.find('*', i + 2)) {
arg.insert(i, 1, '.');
}
return regex(arg);
}
filesystem::path FindFirstFile(filesystem::path directory, filesystem::path::const_iterator& start, const filesystem::path::const_iterator& finish, string& filename) {
while (start != finish && start->string().find('*') == string::npos) {
directory /= *start++;
}
filesystem::directory_iterator it(directory);
filesystem::path result;
if (it != filesystem::directory_iterator()) {
if (start == finish) {
for (auto i = filename.find('.'); i != string::npos; i = filename.find('.', i + 2)) {
filename.insert(i, 1, '\\');
}
const auto re = GenerateRegex(filename);
do {
if (!filesystem::is_directory(it->status()) && regex_match(it->path().string(), re)) {
result = *it;
break;
}
} while (++it != filesystem::directory_iterator());
}
else {
const auto re = GenerateRegex(start->string());
do {
if (it->is_directory() && regex_match(prev(it->path().end())->string(), re)) {
result = FindFirstFile(it->path(), next(start), finish, filename);
if (!result.empty()) {
break;
}
}
} while (++it != filesystem::directory_iterator());
}
}
return result;
}
可通过以下方式调用:
const filesystem::path input("C:/Test/Data*Set/Files*/*.txt");
if (input.is_absolute()) {
const auto relative_parent = input.parent_path().relative_path();
cout << FindFirstFile(input.root_path(), begin(relative_parent), end(relative_parent), input.filename().string()) << endl;
} else {
const auto parent = input.parent_path();
cout << FindFirstFile(filesystem::current_path(), begin(parent), end(parent), input.filename().string()) << endl;
}
const文件系统::路径输入(“C:/Test/Data*Set/Files*/*.txt”);
if(input.is_absolute()){
const auto relative_parent=input.parent_path().relative_path();
cout需要了解FindFirstFile[Ex]
是如何工作的。这已经结束。FindFirstFile[Ex]
需要将输入名称分割为文件夹名称(将作为文件句柄打开),并将搜索掩码用作文件名。掩码只能在文件名中。文件夹必须具有准确的名称,且不带通配符才能首先打开
因此,FindFirstFile[Ex]
始终打开具体的单个文件夹,并通过掩码在此文件夹中搜索。对于递归搜索文件,我们需要递归调用FindFirstFile[Ex]
。通常情况下,我们在所有级别上都使用相同的常量搜索掩码。例如,当我们希望查找所有文件时,都从X:\SomeFolder
开始,我们首先调用FindFirstFile[Ex]
并在级别0上使用X:\SomeFolder\*
调用FindFirstFile[Ex]
在级别1上使用X:\SomeFolder\SomeSubfolder\*
等等。但是我们可以在不同级别上使用不同的搜索掩码。数据*集
在级别0上,文件*
在级别1上,在级别2上使用*.txt
所以我们需要调用FindFirstFileEx
recursive,并且在不同的递归级别上使用不同的掩码。例如,我们希望找到c:\Program*\*\*\*.txt
。我们需要从c:\Program*
开始,然后为每个创建的结果追加\*
掩码,然后在下一级别追加\*.txt
。或者我们可以例如want next-按下一个掩码搜索文件-c:\Program files*\Internet Explorer\*
具有任何深度级别。我们可以使用恒定深度搜索文件夹掩码(可选),最终掩码(也可选)已在所有更深级别上使用。
所有这一切其实都不那么困难和高效:
struct ENUM_CONTEXT : WIN32_FIND_DATA
{
PCWSTR _szMask;
PCWSTR *_pszMask;
ULONG _MaskCount;
ULONG _MaxLevel;
ULONG _nFiles;
ULONG _nFolders;
WCHAR _FileName[MAXSHORT + 1];
void StartEnum(PCWSTR pcszRoot, PCWSTR pszMask[], ULONG MaskCount, PCWSTR szMask, ULONG MaxLevel, PSTR prefix)
{
SIZE_T len = wcslen(pcszRoot);
if (len < RTL_NUMBER_OF(_FileName))
{
memcpy(_FileName, pcszRoot, len * sizeof(WCHAR));
_szMask = szMask, _pszMask = pszMask, _MaskCount = MaskCount;
_MaxLevel = szMask ? MaxLevel : MaskCount;
_nFolders = 0, _nFolders = 0;
Enum(_FileName + len, 0, prefix);
}
}
void Enum(PWSTR pszEnd, ULONG nLevel, PSTR prefix);
};
void ENUM_CONTEXT::Enum(PWSTR pszEnd, ULONG nLevel, PSTR prefix)
{
if (nLevel > _MaxLevel)
{
return ;
}
PCWSTR lpFileName = _FileName;
SIZE_T cb = lpFileName + RTL_NUMBER_OF(_FileName) - pszEnd;
PCWSTR szMask = nLevel < _MaskCount ? _pszMask[nLevel] : _szMask;
SIZE_T cchMask = wcslen(szMask) + 1;
if (cb < cchMask + 1)
{
return ;
}
*pszEnd++ = L'\\', cb--;
DbgPrint("%s[<%.*S>]\n", prefix, pszEnd - lpFileName, lpFileName);
memcpy(pszEnd, szMask, cchMask * sizeof(WCHAR));
ULONG dwError;
HANDLE hFindFile = FindFirstFileEx(lpFileName, FindExInfoBasic, this, FindExSearchNameMatch, 0, FIND_FIRST_EX_LARGE_FETCH);
if (hFindFile != INVALID_HANDLE_VALUE)
{
PWSTR FileName = cFileName;
do
{
SIZE_T FileNameLength = wcslen(FileName);
switch (FileNameLength)
{
case 2:
if (FileName[1] != '.') break;
case 1:
if (FileName[0] == '.') continue;
}
if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
_nFolders++;
if (!(dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
{
if (cb < FileNameLength)
{
__debugbreak();
}
else
{
memcpy(pszEnd, FileName, FileNameLength * sizeof(WCHAR));
Enum(pszEnd + FileNameLength, nLevel + 1, prefix - 1);
}
}
}
else if (nLevel >= _MaskCount || (!_szMask && nLevel == _MaskCount - 1))
{
_nFiles++;
DbgPrint("%s%u%u <%.*S>\n", prefix, nFileSizeLow, nFileSizeHigh, FileNameLength, FileName);
}
} while (FindNextFile(hFindFile, this));
if ((dwError = GetLastError()) == ERROR_NO_MORE_FILES)
{
dwError = NOERROR;
}
FindClose(hFindFile);
}
else
{
dwError = GetLastError();
}
if (dwError && dwError != ERROR_FILE_NOT_FOUND)
{
DbgPrint("%s[<%.*S>] err = %u\n", prefix, pszEnd - lpFileName, lpFileName, dwError);
}
}
void Test(PCWSTR pcszRoot)
{
char prefix[MAXUCHAR + 1];
memset(prefix, '\t', RTL_NUMBER_OF(prefix) - 1);
prefix[RTL_NUMBER_OF(prefix) - 1] = 0;
ENUM_CONTEXT ectx;
static PCWSTR Masks[] = { L"Program*", L"*", L"*.txt" };
static PCWSTR Masks2[] = { L"Program*", L"*" };
static PCWSTR Masks3[] = { L"Program Files*", L"Internet Explorer" };
// search Program*\*\*.txt with fixed deep level
ectx.StartEnum(pcszRoot, Masks, RTL_NUMBER_OF(Masks), 0, RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
// search *.txt files from Program*\*\ - any deep level
ectx.StartEnum(pcszRoot, Masks2, RTL_NUMBER_OF(Masks2), L"*.txt", RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
// search all files (*) from Program Files*\Internet Explorer\
ectx.StartEnum(pcszRoot, Masks3, RTL_NUMBER_OF(Masks3), L"*", RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
}
结构枚举上下文:WIN32查找数据
{
PCWSTR_szMask;
PCWSTR*_pszMask;
乌龙马斯克山;
ULONG_MaxLevel;
乌龙文件;
乌龙文件夹;
WCHAR_文件名[MAXSHORT+1];
void StartEnum(PCWSTR pcszRoot,PCWSTR pszMask[],ULONG MaskCount,PCWSTR szMask,ULONG MaxLevel,PSTR前缀)
{
尺寸长度=wcslen(pcszRoot);
if(len