Как «нормализовать» путь, используя boost :: filesystem? - PullRequest
36 голосов
/ 17 ноября 2009

Мы используем 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

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

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

Более ранние версии Boost имели функцию normalize() - но, похоже, она устарела и удалена (без объяснения причин).

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

Ответы [ 6 ]

30 голосов
/ 17 ноября 2009

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;
}
18 голосов
/ 09 октября 2012

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

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;
        }
    }

    return result;
}
12 голосов
/ 19 октября 2016

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

path lexically_normal(const path& p);
7 голосов
/ 17 ноября 2009

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

Работа в реалиях, описанных ниже.

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

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

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

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

3 голосов
/ 17 ноября 2009

Это все еще там. Продолжайте использовать это.

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

0 голосов
/ 01 мая 2017

Поскольку «каноническая» функция работает только с существующими путями, я создал собственное решение, которое разделяет путь на его части и сравнивает каждую часть со следующей. Я использую это с 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...