A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from http://stackoverflow.com/questions/10167382/boostfilesystem-get-relative-path below:

c++ - boost::filesystem get relative path

Asked 13 years, 4 months ago

Viewed 27k times

What methods of the boost::filesystem library can help me to get a path relative to another path?

I have a path /home/user1/Downloads/Books and a path /home/user1/. Now I want to get a path Downloads/Books.

tshepang

12.5k2525 gold badges9898 silver badges140140 bronze badges

asked Apr 16, 2012 at 0:22

itunitun

3,5411212 gold badges5454 silver badges7575 bronze badges

2

In new versions of boost (starting in 1.60), you can use boost::filesystem::relative. (See the documentation here.)

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

answered Jun 9, 2016 at 0:53

milaniezmilaniez

1,12111 gold badge99 silver badges2323 bronze badges

1

The code in the provided answers is quite long on each line. Assuming that you wrote namespace fs = boost::filesystem; then this code gets you most of the way and looks easier to read to me:

    auto relativeTo( const fs::path& from, const 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.
       auto fromIter = from.begin();
       auto 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)
       auto finalPath = fs::path{};
       while( fromIter != from.end() && *fromIter != ""  )
       {
          finalPath /= "..";
          ++fromIter;
       }
    
       // Append the remainder of the to path (nearest common directory => to)
       while( toIter != to.end() )
       {
          finalPath /= *toIter;
          ++toIter;
       }
    
       return finalPath;
    }
Alan Birtles

37.6k44 gold badges3737 silver badges6969 bronze badges

answered Mar 23, 2015 at 22:21

Robert MassaioliRobert Massaioli

13.5k77 gold badges5959 silver badges7474 bronze badges

4

Taken from a link found by following the ticket Nicol linked to:

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

Stick that in a header file and it should do what you want.

Sample call:

boost::filesystem::path a("foo/bar"), b("foo/test/korv.txt");
std::cout << make_relative( a, b ).string() << std::endl;

answered Apr 16, 2012 at 0:55

Mahmoud Al-QudsiMahmoud Al-Qudsi

29.7k1313 gold badges9393 silver badges130130 bronze badges

2

Sadly, such a function does not exist in Boost.Filesystem. It has been requested, but they don't seem to care.

You basically have to do it manually.

Boost.Filesystem 1.60 added the relative function that can be used to handle this.

answered Apr 16, 2012 at 0:41

Nicol BolasNicol Bolas

479k6565 gold badges853853 silver badges1.1k1.1k bronze badges

3

From C++17 onwards, the solution to this problem is to use std::filesystem::relative for paths that exist, and std::filesystem::path::lexically_relative for paths that might not exist.

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

fs::path path("/home/user1/Downloads/Books");
fs::path base("/home/user1/");
std::cout << fs::relative(path, base) << '\n';
std::cout << path.lexically_relative(base) << '\n';

This prints

"Downloads/Books"
"Downloads/Books"

answered Dec 1, 2021 at 16:13

PBSPBS

1,4241212 silver badges2020 bronze badges

The accepted answer's code doesn't work. It should be

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

answered Mar 23, 2015 at 20:33

Terry ShiTerry Shi

1,02811 gold badge1111 silver badges1414 bronze badges

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4