admin管理员组

文章数量:1435859

I'm trying to detect whether a certain path is under some blacklisted path. I've found examples in Check if an std::filesystem::path is inside a directory thread.

My example is the following (p variables are target paths, and b variables are blacklisted folders):

#include <iostream>
#include <vector>
#include <memory>
#include <filesystem>

int main()
{
    using Path = std::filesystem::path;
    Path p1 = "/folder1/file.txt";
    Path b1 = "/folder1";
    auto finalPath      = b1 / p1;
    auto [iter1, iter2] = std::mismatch(b1.begin(), b1.end(), finalPath.begin());
    if( iter1 == b1.end()){
        std::cout << "Yes\n"; // True
    } else {
        std::cout << "No\n";
    }

    p1        = "/folder1/file.txt";
    b1        = "/folder1/";
    finalPath = b1 / p1;
    auto [iter3, iter4] = std::mismatch(b1.begin(), b1.end(), finalPath.begin());
    if (iter3 == b1.end()) {
        std::cout << "Yes\n";
    } else {
        std::cout << "No\n"; // False
    }

    return 0;
}

In this example the first comparison is true and the second is false. The only difference is trailing path-separator in blacklisted folder. It doesn't seem that second variant returns iterator for the first mismatched symbol. Why that?

Also one of code snippets in above-mentioned thread suggest to compare resulting first iterator to mismatched symbol with the end() - 1 iterator like so:

if (iter3 == std::prev(b1.end())) {

It may help, but cppreference says path's iterator is not necessarily a bidirectional iterator, so, if I understand it correctly, prev may not work, and MSVC explicitly forbids that.

How to fix this code? For brevity I assume that paths don't need to be lexically_normal.

I'm trying to detect whether a certain path is under some blacklisted path. I've found examples in Check if an std::filesystem::path is inside a directory thread.

My example is the following (p variables are target paths, and b variables are blacklisted folders):

#include <iostream>
#include <vector>
#include <memory>
#include <filesystem>

int main()
{
    using Path = std::filesystem::path;
    Path p1 = "/folder1/file.txt";
    Path b1 = "/folder1";
    auto finalPath      = b1 / p1;
    auto [iter1, iter2] = std::mismatch(b1.begin(), b1.end(), finalPath.begin());
    if( iter1 == b1.end()){
        std::cout << "Yes\n"; // True
    } else {
        std::cout << "No\n";
    }

    p1        = "/folder1/file.txt";
    b1        = "/folder1/";
    finalPath = b1 / p1;
    auto [iter3, iter4] = std::mismatch(b1.begin(), b1.end(), finalPath.begin());
    if (iter3 == b1.end()) {
        std::cout << "Yes\n";
    } else {
        std::cout << "No\n"; // False
    }

    return 0;
}

In this example the first comparison is true and the second is false. The only difference is trailing path-separator in blacklisted folder. It doesn't seem that second variant returns iterator for the first mismatched symbol. Why that?

Also one of code snippets in above-mentioned thread suggest to compare resulting first iterator to mismatched symbol with the end() - 1 iterator like so:

if (iter3 == std::prev(b1.end())) {

It may help, but cppreference says path's iterator is not necessarily a bidirectional iterator, so, if I understand it correctly, prev may not work, and MSVC explicitly forbids that.

How to fix this code? For brevity I assume that paths don't need to be lexically_normal.

Share Improve this question asked Nov 16, 2024 at 14:37 qloqqloq 1,2931 gold badge11 silver badges17 bronze badges 5
  • Why not print out the value of finalPath so that you see what you are using when you call std::mismatch. – PaulMcKenzie Commented Nov 16, 2024 at 14:46
  • 1 From the documentation: "If there is a directory separator after the last file-name in the path, the last element before the end iterator is an empty element.". So iterating over "/folder1/" produces three elements: /, folder1 and empty string. Iterating over "/folder1/file.txt" doesn't have the corresponding empty string, hence the mismatch. – Igor Tandetnik Commented Nov 16, 2024 at 14:52
  • So you could try something like if (iter3 == b1.end() || (iter3->empty() && std::next(iter3) == b1.end())) – Igor Tandetnik Commented Nov 16, 2024 at 14:55
  • I suspect you may be able to press lexically_relative into service. It seems that finalPath is not under b1 if and only if finalPath.lexically_relative(b1) starts with ".." – Igor Tandetnik Commented Nov 16, 2024 at 15:04
  • Is the auto finalPath = b1 / p1; intentional? Since p1 is an absolute path, which makes that the same as auto finalPath = p1;. – Eljay Commented Nov 16, 2024 at 15:09
Add a comment  | 

1 Answer 1

Reset to default 2

It's bad practice to have "/folder1/" with a trailing component separator, since that will make an undesirable empty final component for the path.

But if that bad practice is rampant in your code, you could trim it out of base before comparing.

bool is_subpath(std::filesystem::path const& path, std::filesystem::path base) {
    if (base.filename().empty()) base = base.parent_path();
    auto const& [_, base_match] = std::mismatch(path.begin(), path.end(), base.begin(), base.end());
    return base_match == base.end();
}

Or use Igor's approach of iterating over the path to locate the penultimate end before the empty final component. That would allow passing base as a const&.

本文标签: cHow does stdmismatch work with stdfilesystempathStack Overflow