如何使用boost :: filesystem“规范化”路径名?

时间:2009-11-17 01:57:49

标签: c++ boost filesystems

我们在我们的应用程序中使用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 个答案:

答案 0 :(得分:30)

提升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版本

如其他答案中所述,您无法规范化,因为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;
}

答案 1 :(得分:18)

对于boost::filesystem版本3,您还可以尝试通过调用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;
}

答案 2 :(得分:11)

您对canonical的投诉和/或愿望已由Boost 1.60 [1]解决

path lexically_normal(const path& p);

答案 3 :(得分:8)

解释是http://www.boost.org/doc/libs/1_40_0/libs/filesystem/doc/design.htm

  

在下述现实中工作。

     

理由:这不是一个研究项目。需要的是适用于当今平台的东西,包括一些具有有限文件系统的嵌入式操作系统。由于强调可移植性,如果标准化,这样的库将更有用。这意味着能够使用更广泛的平台,只有Unix或Windows及其克隆。

适用于删除normalize的“现实”是:

  

符号链接导致某些路径的规范和普通形式表示不同的文件或目录。例如,给定目录层次结构/ a / b / c,在/名为x的符号链接指向b / c,然后在POSIX路径名解析规则下,路径“/ a / x / ..”应解析为“/ A / b”。如果“/ a / x / ..”首先标准化为“/ a”,则会错误地解析。 (案例由沃尔特兰德里提供。)

库无法真正规范化路径而无法访问底层文件系统,这使得操作a)不可靠b)不可预测c)错误d)以上所有

答案 4 :(得分:3)

它还在那里。继续使用它。

我想他们已弃用它,因为符号链接意味着折叠路径不一定等同。如果c:\full\pathc:\rough的符号链接,那么c:\full\path\..将是c:\,而不是c:\full

答案 5 :(得分:0)

由于“规范”功能仅适用于存在的路径,因此我制作了自己的解决方案,将路径拆分为其部分,并将每个部分与下一部分进行比较。我在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;