Как удалить все вложенные пути?

#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. Ты спасаешь мне жизнь!)) Спасибо!