Вопрос по boost-filesystem, c++, boost, filesystems – boost :: filesystem получает относительный путь

27

Какие методыboost::filesystem библиотека может помочь мне получить путь относительно другого пути?

У меня есть путь/home/user1/Downloads/Books и путь/home/user1/, Теперь я хочу получить путьDownloads/Books.

Новые версии Boost имеютvery simple ответ на это, при условииdown below Drew Dormann
Все остальное терпит неудачу, преобразовывает как абсолютные строки, так и подстроки друг от друга. Andrew T Finnell

Ваш Ответ

5   ответов
28

boost (начиная с 1.60), вы можете использоватьboost::filesystem::relative. (См. Документацию здесь.)

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

int main()
{
    fs::path parentPath("/home/user1/");
    fs::path childPath("/home/user1/Downloads/Books");
    fs::path relativePath = fs::relative(childPath, parentPath);
    std::cout << relativePath << std::endl;
}
6

Sadly, such a function does not exist in Boost.Filesystem. Было запрошено, но они, кажется, не заботятся.

You basically have to do it manually.

Добавлена Boost.Filesystem 1.60relative function что может быть использовано для обработки этого.

Вы правы. Возможно, еще несколько комментариев помогут? :)
На самом деле патч был отклонен по причине (использование append).
Этот ответ больше не является правильным. (См. Вашу ссылку)
@ MahmoudAl-Qudsi: я связан с запросом функции. В комментарии к этому запросу есть исправление, но это не так.part запроса.
@DrewDormann: исправлено.
15

template < >
    path& path::append< typename path::iterator >( typename path::iterator begin, typename path::iterator end, const codecvt_type& cvt)
    { 
        for( ; begin != end ; ++begin )
            *this /= *begin;
        return *this;
    }
    // Return path when appended to a_From will resolve to same as a_To
    boost::filesystem::path make_relative( boost::filesystem::path a_From, boost::filesystem::path a_To )
    {
        a_From = boost::filesystem::absolute( a_From ); a_To = boost::filesystem::absolute( a_To );
        boost::filesystem::path ret;
        boost::filesystem::path::const_iterator itrFrom( a_From.begin() ), itrTo( a_To.begin() );
        // Find common base
        for( boost::filesystem::path::const_iterator toEnd( a_To.end() ), fromEnd( a_From.end() ) ; itrFrom != fromEnd && itrTo != toEnd && *itrFrom == *itrTo; ++itrFrom, ++itrTo );
        // Navigate backwards in directory to reach previously found base
        for( boost::filesystem::path::const_iterator fromEnd( a_From.end() ); itrFrom != fromEnd; ++itrFrom )
        {
            if( (*itrFrom) != "." )
                ret /= "..";
        }
        // Now navigate down the directory branch
        ret.append( itrTo, a_To.end() );
        return ret;
    }

Вставьте это в заголовочный файл, и он должен делать то, что вы хотите.

Образец звонка:

boost::filesystem::path a("foo/bar"), b("foo/test/korv.txt");
std::cout << make_relative( a, b ).string() << std::endl;
Код работает хорошо. Параметр cvt не используется и может быть удален.
Нет необходимости готовитьсяmake_relative в этом примере вызова. Это может быть неправильно (данный код не выглядит так, как будтоmake_relative вboost::filesystem) и если это правильно, то излишне из-за ADL.
1

namespace boost { namespace filesystem {

template <> path& path::append<path::iterator>(path::iterator begin, path::iterator end, const codecvt_type& cvt)
{
    for( ; begin != end ; ++begin )
        *this /= *begin;
    return *this;
}

// Return path when appended to a_From will resolve to same as a_To
boost::filesystem::path make_relative( boost::filesystem::path a_From, boost::filesystem::path a_To )
{
    a_From = boost::filesystem::absolute( a_From ); a_To = boost::filesystem::absolute( a_To );
    boost::filesystem::path ret;
    boost::filesystem::path::const_iterator itrFrom( a_From.begin() ), itrTo( a_To.begin() );
    // Find common base
    for( boost::filesystem::path::const_iterator toEnd( a_To.end() ), fromEnd( a_From.end() ) ; itrFrom != fromEnd && itrTo != toEnd && *itrFrom == *itrTo; ++itrFrom, ++itrTo );
    // Navigate backwards in directory to reach previously found base
    for( boost::filesystem::path::const_iterator fromEnd( a_From.end() ); itrFrom != fromEnd; ++itrFrom )
    {
        if( (*itrFrom) != "." )
            ret /= "..";
    }
    // Now navigate down the directory branch
    ret.append( itrTo, a_To.end() );
    return ret;
}

} } // namespace boost::filesystem
17

я, что вы написалиnamespace fs = boost::filesystem; тогда этот код дает вам большую часть пути и выглядит легче для чтения для меня:

static fs::path relativeTo(fs::path from, fs::path to)
{
   // Start at the root path and while they are the same then do nothing then when they first
   // diverge take the entire from path, swap it with '..' segments, and then append the remainder of the to path.
   fs::path::const_iterator fromIter = from.begin();
   fs::path::const_iterator toIter = to.begin();

   // Loop through both while they are the same to find nearest common directory
   while (fromIter != from.end() && toIter != to.end() && (*toIter) == (*fromIter))
   {
      ++toIter;
      ++fromIter;
   }

   // Replace from path segments with '..' (from => nearest common directory)
   fs::path finalPath;
   while (fromIter != from.end())
   {
      finalPath /= "..";
      ++fromIter;
   }

   // Append the remainder of the to path (nearest common directory => to)
   while (toIter != to.end())
   {
      finalPath /= *toIter;
      ++toIter;
   }

   return finalPath;
}
это сработало для меня
Это выглядит намного лучше, чем другие ответы.

Похожие вопросы