Подтвердить что ты не робот

Как "нормализовать" путь с помощью boost:: filesystem?

Мы используем boost:: filesystem в нашем приложении. У меня есть "полный" путь, который создается путем объединения нескольких путей:

#include <boost/filesystem/operations.hpp>
#include <iostream>
     
namespace bf = boost::filesystem;

int main()
{
    bf::path root("c:\\some\\deep\\application\\folder");
    bf::path subdir("..\\configuration\\instance");
    bf::path cfgfile("..\\instance\\myfile.cfg");

    bf::path final ( root / subdir / cfgfile);

    cout << final.file_string();
}

Окончательный путь печатается как:

c:\some\deep\application\folder\..\configuration\instance\..\instance\myfile.cfg

Это допустимый путь, но когда я показываю его пользователю, я бы предпочел его нормализовать. (Примечание. Я даже не уверен, что "нормализованное" является правильным словом для этого). Вот так:

c:\some\deep\application\configuration\instance\myfile.cfg

В более ранних версиях Boost была функция normalize(), но она, кажется, устарела и удалена (без каких-либо объяснений).

Есть ли причина, по которой я не должен использовать макрос BOOST_FILESYSTEM_NO_DEPRECATED? Есть ли альтернативный способ сделать это с помощью библиотеки Boost Filesystem? Или я должен написать код для непосредственного управления контуром в виде строки?

4b9b3361

Ответ 1

Boost v1.48 и выше

Вы можете использовать boost::filesystem::canonical:

path canonical(const path& p, const path& base = current_path());
path canonical(const path& p, system::error_code& ec);
path canonical(const path& p, const path& base, system::error_code& ec);

http://www.boost.org/doc/libs/1_48_0/libs/filesystem/v3/doc/reference.html#canonical

v1.48 и выше также предоставляют функцию boost::filesystem::read_symlink для разрешения символических ссылок.

Повышение версии до v1.48

Как упоминалось в других ответах, вы не можете нормализовать, потому что boost :: filesystem не может переходить по символическим ссылкам. Однако вы можете написать функцию, которая нормализует "насколько это возможно" (при условии, что "." И ".." обрабатываются нормально), поскольку boost предлагает возможность определить, является ли файл символической ссылкой.

То есть, если родительский элемент для ".." является символической ссылкой, вы должны сохранить ее, в противном случае ее, вероятно, можно удалить, и, вероятно, всегда безопасно удалить ".".

Это похоже на манипулирование реальной строкой, но немного более элегантно.

boost::filesystem::path resolve(
    const boost::filesystem::path& p,
    const boost::filesystem::path& base = boost::filesystem::current_path())
{
    boost::filesystem::path abs_p = boost::filesystem::absolute(p,base);
    boost::filesystem::path result;
    for(boost::filesystem::path::iterator it=abs_p.begin();
        it!=abs_p.end();
        ++it)
    {
        if(*it == "..")
        {
            // /a/b/.. is not necessarily /a if b is a symbolic link
            if(boost::filesystem::is_symlink(result) )
                result /= *it;
            // /a/b/../.. is not /a/b/.. under most circumstances
            // We can end up with ..s in our result because of symbolic links
            else if(result.filename() == "..")
                result /= *it;
            // Otherwise it should be safe to resolve the parent
            else
                result = result.parent_path();
        }
        else if(*it == ".")
        {
            // Ignore
        }
        else
        {
            // Just cat other path entries
            result /= *it;
        }
    }
    return result;
}

Ответ 2

В версии 3 boost::filesystem вы также можете попытаться удалить все символические ссылки, вызвав canonical. Это можно сделать только для существующих путей, поэтому для функции, которая также работает для несуществующих, потребуется два шага (протестировано на MacOS Lion и обновлено для Windows благодаря комментарию @void.pointer):

boost::filesystem::path normalize(const boost::filesystem::path &path) {
    boost::filesystem::path absPath = absolute(path);
    boost::filesystem::path::iterator it = absPath.begin();
    boost::filesystem::path result = *it++;

    // Get canonical version of the existing part
    for (; exists(result / *it) && it != absPath.end(); ++it) {
        result /= *it;
    }
    result = canonical(result);

    // For the rest remove ".." and "." in a path with no symlinks
    for (; it != absPath.end(); ++it) {
        // Just move back on ../
        if (*it == "..") {
            result = result.parent_path();
        }
        // Ignore "."
        else if (*it != ".") {
            // Just cat other path entries
            result /= *it;
        }
    }

    // Make sure the dir separators are correct even on Windows
    return result.make_prefered();
}

Ответ 3

Ваши жалобы и/или пожелания о canonical были рассмотрены Boost 1.60 [1] с

path lexically_normal(const path& p);

Ответ 4

объяснение находится на http://www.boost.org/doc/libs/1_40_0/libs/filesystem/doc/design.htm:

Работайте над реалиями, описанными ниже.

Обоснование: это не исследовательский проект. Потребность в том, что работает на сегодняшних платформах, включая некоторые встроенные операционные системы с ограниченными файловыми системами. Из-за акцента на переносимость такая библиотека была бы гораздо более полезной, если бы она была стандартизирована. Это означает возможность работать с гораздо более широким диапазоном платформ, которые просто Unix или Windows и их клоны.

где "реальность", применимая к удалению normalize, такова:

Символьные ссылки вызывают каноническую и нормальную форму некоторых путей для представления разных файлов или каталогов. Например, с учетом иерархии каталогов /a/b/c с символической ссылкой в ​​/по имени x, указывающей на b/c, тогда в соответствии с правилами разрешения пути POSIX путь "/a/x/.." должен быть разрешен "/A/B". Если "/a/x/.." были сначала нормированы на "/a", это будет неправильно исправлено. (Дело предоставлено Уолтером Лэндри.)

библиотека не может реально нормализовать путь без доступа к базовым файловым системам, что делает операцию a) ненадежной b) непредсказуемой c) неправильной d) все вышеперечисленное

Ответ 5

Он все еще там. Продолжайте использовать его.

Я предполагаю, что они устарели, потому что символические ссылки означают, что свернутый путь не обязательно эквивалентен. Если c:\full\path была символической ссылкой на c:\rough, то c:\full\path\.. будет c:\, а не c:\full.

Ответ 6

Поскольку "каноническая" функция работает только с существующими путями, я создал собственное решение, которое разбивает путь на его части и сравнивает каждую часть со следующей. Я использую это с Boost 1.55.

typedef boost::filesystem::path PathType;

template <template <typename T, typename = std::allocator<T> > class Container>
Container<PathType> SplitPath(const PathType& path)
{
    Container<PathType> ret;
    long the_size = std::distance(path.begin(),path.end());
    if(the_size == 0)
        return Container<PathType>();
    ret.resize(the_size);
    std::copy(path.begin(),path.end(),ret.begin());
    return ret;
}

PathType NormalizePath(const PathType& path)
{
    PathType ret;
    std::list<PathType> splitPath = SplitPath<std::list>(path);
    for(std::list<PathType>::iterator it = (path.is_absolute() ? ++splitPath.begin() : splitPath.begin()); it != splitPath.end(); ++it)
    {
        std::list<PathType>::iterator it_next = it;
        ++it_next;
        if(it_next == splitPath.end())
            break;
        if(*it_next == "..")
        {
            it = splitPath.erase(it);
            it = splitPath.erase(it);
        }
    }
    for(std::list<PathType>::iterator it = splitPath.begin(); it != splitPath.end(); ++it)
    {
        ret /= *it;
    }
    return ret;
}

Чтобы использовать это, вот пример того, как вы его назовёте:

std::cout<<NormalizePath("/home/../home/thatfile/")<<std::endl;