如何使用boost:::文件系统“规范化”路径名?

时间:2022-09-24 23:12:44

We are using boost::filesystem in our application. I have a 'full' path that is constructed by concatenating several paths together:

我们在应用程序中使用boost::文件系统。我有一条“完整”路径,它是通过将多条路径连接在一起构建的:

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

The final path is printed as:

最终路径打印为:

c:\some\deep\application\folder\..\configuration\instance\..\instance\myfile.cfg

This is a valid path, but when I display it to the user I'd prefer it to be normalized. (Note: I'm not even sure if "normalized" is the correct word for this). Like this:

这是一个有效的路径,但是当我将它显示给用户时,我希望它是规范化的。(注意:我甚至不确定“规范化”是否正确)。是这样的:

c:\some\deep\application\configuration\instance\myfile.cfg

Earlier versions of Boost had a normalize() function - but it seems to have been deprecated and removed (without any explanation).

Boost的早期版本具有normalize()功能——但似乎已经弃用并删除了(没有任何解释)。

Is there a reason I should not use the BOOST_FILESYSTEM_NO_DEPRECATED macro? Is there an alternative way to do this with the Boost Filesystem library? Or should I write code to directly manipulating the path as a string?

是否有理由不使用BOOST_FILESYSTEM_NO_DEPRECATED宏?对于Boost文件系统库,是否有其他方法来实现这一点?或者我应该编写代码直接将路径作为字符串进行操作吗?

6 个解决方案

#1


30  

Boost v1.48 and above

You can use boost::filesystem::canonical:

boost::文件系统::您可以使用规范:

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

http://www.boost.org/doc/libs/1_48_0/libs/filesystem/v3/doc/reference.html规范

v1.48 and above also provide the boost::filesystem::read_symlink function for resolving symbolic links.

v1.48和以上版本还提供了boost:::文件系统:read_symlink函数,用于解析符号链接。

Boost versions prior to v1.48

As mentioned in other answers, you can't normalise because boost::filesystem can't follow symbolic links. However, you can write a function that normalises "as much as possible" (assuming "." and ".." are treated normally) because boost offers the ability to determine whether or not a file is a symbolic link.

正如在其他答案中提到的,您不能规范化,因为boost::文件系统不能遵循符号链接。但是,您可以编写一个函数,使之“尽可能”规范化(假设“.”和“.”得到正常处理),因为boost提供了确定文件是否为符号链接的能力。

That is to say, if the parent of the ".." is a symbolic link then you have to retain it, otherwise it is probably safe to drop it and it's probably always safe to remove ".".

也就是说,如果“.. .”的父结点是一个符号链接,那么你就必须保留它,否则删除它可能是安全的,删除它也可能是安全的。

It's similar to manipulating the actual string, but slightly more elegant.

它类似于操作实际的字符串,但稍微优雅一些。

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

#2


18  

With version 3 of boost::filesystem you can also try to remove all the symbolic links with a call to canonical. This can be done only for existing paths so a function that also works for non-existing ones would require two steps (tested on MacOS Lion):

在版本3的boost::文件系统中,您还可以尝试删除所有的符号链接,并调用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;
}

#3


9  

Your complaints and/or wishes about canonical have been addressed by Boost 1.60 [1] with

你对canonical公司的投诉和/或愿望已通过Boost 1.60[1]得到解决

path lexically_normal(const path& p);

#4


8  

the explanation is at http://www.boost.org/doc/libs/1_40_0/libs/filesystem/doc/design.htm :

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

Work within the realities described below.

在下面描述的现实中工作。

Rationale: This isn't a research project. The need is for something that works on today's platforms, including some of the embedded operating systems with limited file systems. Because of the emphasis on portability, such a library would be much more useful if standardized. That means being able to work with a much wider range of platforms that just Unix or Windows and their clones.

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

where the "reality" applicable to removal of normalize is:

适用于消除规范的“现实”是:

Symbolic links cause canonical and normal form of some paths to represent different files or directories. For example, given the directory hierarchy /a/b/c, with a symbolic link in /a named x pointing to b/c, then under POSIX Pathname Resolution rules a path of "/a/x/.." should resolve to "/a/b". If "/a/x/.." were first normalized to "/a", it would resolve incorrectly. (Case supplied by Walter Landry.)

符号链接导致某些路径的规范和正常形式,以表示不同的文件或目录。例如,给定目录层次结构/a/b/c,在/a中有一个符号链接指向b/c,然后在POSIX路径名解析规则下,“/a/x/.. .”的路径应该解析为“/a/b”。如果“/a/x/..”首先被规范化为“/a”,它将不正确地解析。(由沃尔特·兰德里提供。)

the library cannot really normalize a path without access to the underlying filesystems, which makes the operation a) unreliable b) unpredictable c) wrong d) all of the above

如果没有对底层文件系统的访问,库就不能真正地规范化路径,这使得操作a)不可靠b)不可预测c)错误d)以上所有的一切

#5


3  

It's still there. Keep using it.

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

I imagine they deprecated it because symbolic links mean that the collapsed path isn't necessarily equivalent. If c:\full\path were a symlink to c:\rough, then c:\full\path\.. would be c:\, not c:\full.

我猜想他们不赞成它,因为符号链接意味着崩溃的路径不一定是等价的。如果c:\full\path与c:\rough,那么c:\full\path是c:\,不是c:\满了。

#6


0  

Since the "canonical" function works only with paths that exist, I made my own solution that splits the path to its parts, and compares every part with the next one. I'm using this with Boost 1.55.

由于“规范”函数只对存在的路径起作用,因此我提出了自己的解决方案,将路径分割到各个部分,并将每个部分与下一个部分进行比较。我用的是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;
}

To use this, here's an example on how you call it:

要使用这个,这里有一个例子,你如何称呼它:

std::cout<<NormalizePath("/home/../home/thatfile/")<<std::endl;

#1


30  

Boost v1.48 and above

You can use boost::filesystem::canonical:

boost::文件系统::您可以使用规范:

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

http://www.boost.org/doc/libs/1_48_0/libs/filesystem/v3/doc/reference.html规范

v1.48 and above also provide the boost::filesystem::read_symlink function for resolving symbolic links.

v1.48和以上版本还提供了boost:::文件系统:read_symlink函数,用于解析符号链接。

Boost versions prior to v1.48

As mentioned in other answers, you can't normalise because boost::filesystem can't follow symbolic links. However, you can write a function that normalises "as much as possible" (assuming "." and ".." are treated normally) because boost offers the ability to determine whether or not a file is a symbolic link.

正如在其他答案中提到的,您不能规范化,因为boost::文件系统不能遵循符号链接。但是,您可以编写一个函数,使之“尽可能”规范化(假设“.”和“.”得到正常处理),因为boost提供了确定文件是否为符号链接的能力。

That is to say, if the parent of the ".." is a symbolic link then you have to retain it, otherwise it is probably safe to drop it and it's probably always safe to remove ".".

也就是说,如果“.. .”的父结点是一个符号链接,那么你就必须保留它,否则删除它可能是安全的,删除它也可能是安全的。

It's similar to manipulating the actual string, but slightly more elegant.

它类似于操作实际的字符串,但稍微优雅一些。

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

#2


18  

With version 3 of boost::filesystem you can also try to remove all the symbolic links with a call to canonical. This can be done only for existing paths so a function that also works for non-existing ones would require two steps (tested on MacOS Lion):

在版本3的boost::文件系统中,您还可以尝试删除所有的符号链接,并调用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;
}

#3


9  

Your complaints and/or wishes about canonical have been addressed by Boost 1.60 [1] with

你对canonical公司的投诉和/或愿望已通过Boost 1.60[1]得到解决

path lexically_normal(const path& p);

#4


8  

the explanation is at http://www.boost.org/doc/libs/1_40_0/libs/filesystem/doc/design.htm :

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

Work within the realities described below.

在下面描述的现实中工作。

Rationale: This isn't a research project. The need is for something that works on today's platforms, including some of the embedded operating systems with limited file systems. Because of the emphasis on portability, such a library would be much more useful if standardized. That means being able to work with a much wider range of platforms that just Unix or Windows and their clones.

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

where the "reality" applicable to removal of normalize is:

适用于消除规范的“现实”是:

Symbolic links cause canonical and normal form of some paths to represent different files or directories. For example, given the directory hierarchy /a/b/c, with a symbolic link in /a named x pointing to b/c, then under POSIX Pathname Resolution rules a path of "/a/x/.." should resolve to "/a/b". If "/a/x/.." were first normalized to "/a", it would resolve incorrectly. (Case supplied by Walter Landry.)

符号链接导致某些路径的规范和正常形式,以表示不同的文件或目录。例如,给定目录层次结构/a/b/c,在/a中有一个符号链接指向b/c,然后在POSIX路径名解析规则下,“/a/x/.. .”的路径应该解析为“/a/b”。如果“/a/x/..”首先被规范化为“/a”,它将不正确地解析。(由沃尔特·兰德里提供。)

the library cannot really normalize a path without access to the underlying filesystems, which makes the operation a) unreliable b) unpredictable c) wrong d) all of the above

如果没有对底层文件系统的访问,库就不能真正地规范化路径,这使得操作a)不可靠b)不可预测c)错误d)以上所有的一切

#5


3  

It's still there. Keep using it.

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

I imagine they deprecated it because symbolic links mean that the collapsed path isn't necessarily equivalent. If c:\full\path were a symlink to c:\rough, then c:\full\path\.. would be c:\, not c:\full.

我猜想他们不赞成它,因为符号链接意味着崩溃的路径不一定是等价的。如果c:\full\path与c:\rough,那么c:\full\path是c:\,不是c:\满了。

#6


0  

Since the "canonical" function works only with paths that exist, I made my own solution that splits the path to its parts, and compares every part with the next one. I'm using this with Boost 1.55.

由于“规范”函数只对存在的路径起作用,因此我提出了自己的解决方案,将路径分割到各个部分,并将每个部分与下一个部分进行比较。我用的是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;
}

To use this, here's an example on how you call it:

要使用这个,这里有一个例子,你如何称呼它:

std::cout<<NormalizePath("/home/../home/thatfile/")<<std::endl;