I'm looking for a way to check if 2 strings are the same in terms of filesystem path (directory). For example all string from this set are the same in terms of filesystem path: {/x
,\x
,//x
,/x/
}, but this two - /x
and /y
are not, even if /y
is symbolic link to /x
. The program I'm going to write should work as for Linux as well for windows, so I'm looking for portable solution.
EDIT:
I'm using only header-only libs of boost, so solution with boost::filesystem
is not ok for me. I know that there is UrlCompare
in windows API, is there something like in linux?
Any non-Boost solution will involve system dependent code (which is
hidden in Boost if you use Boost). And you will have to define exactly
what you mean by "matching": should "./MyFile.xxx"
and "MyFile.xxx"
compare equal? What about "aaa/.../MyFile.xxx"
and "MyFile.xxx"
?
The way I would handle this is to define a class with two data members,
an std::string
with the “prefix” (which would always be
empty in Unix), and an std::vector<std::string>
with all of the path
elements. This class would be doted with the necessary comparison
functions, and would use system dependent code to implement the
constructor; the constructor itself would be in the source file, and the
source file would include a machine dependent header (generally by using
a separate directory for each variant, and choosing the header by means
of -I
or /I
to specify which directory to use). The sort of things
which might go into the header:
inline bool
isPathSeparator( char ch )
{
return ch == '/';
}
std::string
getHeader( std::string const& fullPathName )
{
return "";
}
bool
charCompare( char lhs, char rhs )
{
return lhs < rhs;
}
bool
charMatch( char lhs, char rhs )
{
return lhs == rhs;
}
for Unix, with:
inline bool
isPathSeparator( char ch )
{
return ch == '/' || ch == '\\';
}
std::string
getHeader( std::string const& fullPathName )
{
return fullPathName.size() > 2 && fullPathName[1] == ':'
? fullPathName.substr( 0, 2 )
: std::string();
}
bool
charCompare( char lhs, char rhs )
{
return tolower( (unsigned char)lhs) < tolower( (unsigned char)rhs );
}
bool
charMatch( char lhs, char rhs )
{
return tolower( (unsigned char)lhs ) == tolower( (unsigned char)rhs );
}
for Windows.
The constructor would then use getHeader
to initialize the header, and
iterate over input.begin() + header.size()
and input.end()
, breaking
the string up into elements. If you encounter an element of "."
,
ignore it, and for one of ".."
, use pop_back()
to remove the top
element of the path, provided the path isn't empty. Afterwards, it's
just a question of defining the comparators to use charCompare
and
charMatch
for char
, and std::lexicographical_compare
or
std::equal
(after verifying that the sizes are equal) with the
comparator for std::string
(and probably furthermore for your new
class). Something like:
struct FileNameCompare
{
bool operator()( char lhs, char rhs ) const
{
return charCompare( lhs, rhs );
}
bool operator()( std::string const& lhs, std::string const& rhs ) const
{
return std::lexicographical_compare(
lhs.begin(), lhs.end(),
rhs.begin(), rhs.end(),
*this );
}
bool operator()( FileName const& lhs, FileName const& rhs ) const
{
return (*this)( lhs.prefix, rhs.prefix )
|| ( !(*this)( rhs.prefix, lhs.prefix )
&& std::lexicographical_compare(
lhs.elements.begin(), lhs.elements.end(),
rhs.elements.begin(), rhs.elements.end(),
*this ) );
}
};
struct FileNameMatch
{
bool operator()( char lhs, char rhs ) const
{
return charMatch( lhs, rhs );
}
bool operator()( std::string const& lhs, std::string const& rhs ) const
{
return lhs.size() == rhs.size()
&& std::equal( lhs.begin(), lhs.end(), rhs.begin(), *this );
}
bool operator()( FileName const& lhs, FileName const& rhs ) const
{
return (*this)( lhs.prefix, rhs.prefix )
&& lhs.elements.size() == rhs.elements.size()
&& std::equal( lhs.elements.begin(), lhs.elements.end(),
rhs.elements.begin(),
*this );
}
};
should do the trick. (Just remember that the operator()( char, char )
const
must be in the source file; you can't inline them in the header,
which won't include the system dependent header which defines
charCompare
and charMatch
.)
std::string path1 = "c:\\folder\\";
std::string path2 = "c:\\folder\\folder\\..\\";
boost::filesystem::equivalent(boost::filesystem::path(path1), boost::filesystem::path(path2)
The code returns true
, because the folder are actually the same.
Use the boost::filesystem library - this has path comparison functionality.
EDIT: You could try the apr - yes it's not C++, however it will be portable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With