#c
Вопрос:
Я новичок в C (и я думаю, что должен быть гораздо более короткий способ сделать то, что мне нужно), но то, что мне нужно сделать, это:
У меня есть несколько vector
путей к папкам
C:\Sessions\MyFolder
C:\Sessions\Calib
C:\Sessions\Calib\2020_04_30_18_02
C:\Sessions\Calib\2020_04_30_18_03
C:\Sessions\Calib\2020_04_30_18_02\test
C:\Sessions\Calib\777\folder
Мне нужно удалить все пути, которые являются подпутями конечного пути. Это значит, что в конце концов мне нужно получить такой результат
C:\Sessions\MyFolder
C:\Sessions\Calib\2020_04_30_18_03
C:\Sessions\Calib\2020_04_30_18_02\test
C:\Sessions\Calib\777\folder
Эти пути были удалены
C:\Sessions\Calib\2020_04_30_18_02
C:\Sessions\Calib
Потому что этот путь C:\Sessions\Calib
является подпутью C:\Sessions\Calib\2020_04_30_18_02
, и на самом деле этот путь C:\Sessions\Calib\2020_04_30_18_02
является подпутью C:\Sessions\Calib\2020_04_30_18_02\test
.
Этот путь C:\Sessions\Calib\2020_04_30_18_02\test
не имеет подпространства в данном списке путей, поэтому мы оставляем его.
Я написал такой метод
/*static*/ std::vector<std::string> Utils::remove_sub_folders(std::vector<std::string> folderPaths_in)
{
std::vector<std::string> resu<
std::vector<std::string> executed_paths;
std::vector<std::string> folder_paths = folderPaths_in;
std::sort(folder_paths.begin(), folder_paths.end());
for each (auto amp; path in folder_paths)
{
std::vector<std::string> path_split = Utils::split(path, "\");
for each (auto amp; compare_path in folder_paths)
{
bool is_have_alredy_processed = false;
for each (auto amp; processed_path in executed_paths)
{
if (compare_path == processed_path)
{
is_have_alredy_processed = true;
break;
}
}
if (!is_have_alredy_processed)
{
std::vector<std::string> compare_path_split = Utils::split(compare_path, "\");
int path_split_size = static_cast<int>(path_split.size());
int path_compare_size = static_cast<int>(compare_path_split.size());
if (path != compare_path amp;amp; path_compare_size >= path_split_size)
{
int min_size = min(path_split_size, path_compare_size);
bool is_equal_begin = true;
for (int i = 0; i < min_size; i )
{
std::string path_word = path_split[i];
std::string compare_word = compare_path_split[i];
if (path_word != compare_word)
{
is_equal_begin = false;
break;
}
}
if ((!is_equal_begin amp;amp; path_split_size == path_compare_size))
{
result.push_back(path);
}
}
}
}
executed_paths.push_back(path);
}
return resu<
}
Я не могу понять, чего мне не хватает, но на самом деле результат, который я получаю, таков :
C:SessionsCalib
C:SessionsCalib2020_04_30_18_02
C:SessionsCalib2020_04_30_18_02test
Вот как я называю этот метод:
std::vector<std::string> all_dirs_by_path{
"C:\Sessions\MyFolder",
"C:\Sessions\Calib",
"C:\Sessions\Calib\2020_04_30_18_02",
"C:\Sessions\Calib\2020_04_30_18_03",
"C:\Sessions\Calib\2020_04_30_18_02\test",
"C:\Sessions\Calib\777\folder",
};
std::vector<std::string> final_dirs_by_path = Utils::remove_sub_folders(all_dirs_by_path);
Что я делаю не так?
Ответ №1:
Вы начинаете с сортировки заданных путей. Однако порядок, в котором он сортируется, скорее всего, не такой, как вы ожидаете. Причина в том, что сортировка std::string
s не обрабатывает разделитель каталогов
каким-либо особым образом. И
сортирует позже, чем символы верхнего регистра, но раньше, чем символы нижнего регистра. Что вам нужно, так это функция сравнения, которая немного лучше разбирается в путях и сортирует их, рассматривая каждый компонент пути отдельно.
Если вы можете использовать библиотеку файловой системы C 17 (или библиотеку файловой системы Boost), вам следует создать вектор std::filesystem::path
s. У них есть operator<()
приложение, которое сравнивает пути так, как вы ожидаете. Как только вы их отсортируете, у вас появится хорошее свойство, что любой родительский путь будет предшествовать дочернему пути, и между родительским и дочерним путями нет путей, у которых нет одного и того же родителя:
C:Foo
C:FooBar
C:FooBaz
C:Quux
...
Таким образом, это довольно просто, пройдите по списку и на любом пути в списке проверьте, является ли следующий путь в списке дочерним по отношению к текущему пути. Если это так, вы можете отказаться от текущего пути.
using namespace fs = std::filesystem; // or use boost::filesystem here
static bool is_parent(const fs::path amp;parent, const fs::path amp;child) {
auto parent_size = std::distance(parent.begin(), parent.end());
auto child_size = std::distance(child.begin(), child.end());
return child_size > parent_size amp;amp; std::equal(parent.begin(), parent.end(), child.begin());
}
std::vector<fs::path> remove_sub_folders(std::vector<fs::path> paths) {
std::sort(paths.begin(), paths.end());
std::vector<fs::path> resu<
for(auto cur = paths.begin(); cur != paths.end(); cur) {
auto next = std::next(cur);
if (next == paths.end() || !is_parent(*cur, *next))
result.push_back(*cur);
}
return resu<
}
Если вы хотите сделать это с std::string
помощью , то вам нужно написать пользовательскую функцию сравнения, которая делает то, что std::filesystem::path::operator<()
делает, и передать ее std::sort()
. Чтобы проверить, является ли один из них родителем другого, убедитесь, что в родительском пути есть разделитель конечных каталогов, а затем просто проверьте, начинается ли дочерний путь с родительского пути.
Комментарии:
1. Ты спасаешь мне жизнь!)) Спасибо!