如何在C++中比较(目录)路径?

5
我正在寻找一种方法来检查两个字符串是否在文件系统路径(目录)方面相同。例如,此集合中的所有字符串在文件系统路径方面都是相同的:{/x\x//x/x/},但是这两个字符串 - /x/y不同,即使/y/x的符号链接。我要编写的程序应该适用于Linux和Windows,因此我正在寻找可移植的解决方案。

编辑:

我仅使用boost的头文件库,因此对于我来说,具有boost :: filesystem的解决方案不可行。我知道Windows API中有UrlCompare,那么Linux中是否有类似的东西呢?


2
+1 好问题。除了 boost,还有其他的替代品吗? - Walter
你现在正在使用仅包含头文件的 Boost,但这是否是不添加库依赖关系的原因? - Lightness Races in Orbit
@Tomalak Garet'kal:由于在Sun Studio 5.7中编译boost存在一些问题。 - Mihran Hovsepyan
听起来是个好理由。 :) 谢谢 - Lightness Races in Orbit
新的C++17标准库中的std::filesystem::canonical可能会有所帮助: https://dev59.com/1loU5IYBdhLWcg3w7aCP#56674900 - MasterHD
4个回答

3
任何非 Boost 解决方案都将涉及系统相关代码(如果您使用 Boost,则这些代码会隐藏起来)。您需要确切地定义“匹配”的含义:像 "./MyFile.xxx""MyFile.xxx" 是否应该相等?"aaa/.../MyFile.xxx""MyFile.xxx" 呢?
我处理此问题的方法是定义一个类,其中包含两个数据成员:带有“前缀”的 std::string(在 Unix 中始终为空),以及带有所有路径元素的 std::vector<std::string>。此类会添加必要的比较函数,并使用系统相关代码实现构造函数;构造函数本身将在源文件中,源文件将包含机器相关的头文件(通常使用独立的目录为每个变体选择头文件,通过使用 -I/I 来指定使用哪个目录)。可能放入头文件的内容包括:
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;
}

适用于Unix系统,使用:

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

适用于Windows。

构造函数将使用getHeader来初始化头文件,并迭代input.begin() + header.size()input.end(),将字符串分解成元素。如果遇到"."元素,则忽略它,如果遇到".."元素,则使用pop_back()删除路径的顶部元素,前提是路径不为空。之后,只需要定义比较器以使用charComparecharMatch进行char比较,并使用std::lexicographical_comparestd::equal(在验证大小相等后)与std::string的比较器(可能还要为您的新类)。像这样:

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

应该就可以了。 (只要记住 operator()( char, char ) const 必须在源文件中;您不能将它们内联到标头中,因为标头不包括定义 charComparecharMatch 的系统相关标头。)


3
std::string path1 = "c:\\folder\\";
std::string path2 = "c:\\folder\\folder\\..\\";

boost::filesystem::equivalent(boost::filesystem::path(path1), boost::filesystem::path(path2)

这段代码返回 true,因为文件夹实际上是相同的。


OP在问题中说:“所以使用boost::filesystem的解决方案对我来说不可行”,正在寻找替代方案。 - Mark Taylor

2
使用boost::filesystem库 - 它具有路径比较功能。
编辑:你可以尝试apr - 是的,它不是C++,但它将是可移植的。

1

就可移植性而言,Boost文件系统库可能会很有用(文档链接)。更具体地说:path.hpp是最有用的,您可以使用路径比较。

编辑

这个在gmane.org上的讨论涉及boost::filesystem的最小头文件版本。总之:这不存在。因此,您将最终构建自己的抽象库并赋予它跨平台功能。

在Linux下,有一个POSIX C库dirent.h。对于Windows,您必须使用Win32 API。但是,还有一个所谓的“目录流库”,似乎是跨平台的,可以在这里找到(网站为德语)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接