我如何“正常化”使用 boost::filesystem 的路径名?

发布于 2024-08-11 00:47:20 字数 969 浏览 4 评论 0原文

我们在应用程序中使用 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

这是一个有效的路径,但当我将其显示给用户时,我希望将其标准化。< /em> (注意:我什至不确定“标准化”是否是正确的词)。像这样:

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

早期版本的 Boost 有一个 normalize() 函数 - 但它似乎已被弃用并删除(没有任何解释)。

我是否有理由不应该使用 BOOST_FILESYSTEM_NO_DEPRECATED 宏?是否有其他方法可以使用 Boost Filesystem 库来执行此操作?或者我应该编写代码来直接将路径作为字符串进行操作?

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

#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).

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

不喜欢何必死缠烂打 2024-08-18 00:47:20

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

Boost v1.48 and above

You can use 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 and above also provide the boost::filesystem::read_symlink function for resolving symbolic links.

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.

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;
}
未蓝澄海的烟 2024-08-18 00:47:20

使用 boost::filesystem 版本 3,您还可以尝试通过调用 canonical 来删除所有符号链接。这只能针对现有路径完成,因此也适用于不存在路径的函数需要两个步骤(在 MacOS Lion 上进行测试,并根据 @void.pointer 的评论针对 Windows 进行更新):

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

    // Make sure the dir separators are correct even on Windows
    return result.make_preferred();
}

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 and updated for Windows thanks to @void.pointer's comment):

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

    // Make sure the dir separators are correct even on Windows
    return result.make_preferred();
}
2024-08-18 00:47:20

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

path lexically_normal(const path& p);

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

path lexically_normal(const path& p);
我很坚强 2024-08-18 00:47:20

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

在下述现实范围内开展工作。

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

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

符号链接导致某些路径的规范和正常形式代表不同的文件或目录。例如,给定目录层次结构 /a/b/c,/a 中名为 x 的符号链接指向 b/c,则根据 POSIX 路径名解析规则,路径“/a/x/..”应解析为“/a/b”。如果“/a/x/..”首先标准化为“/a”,它将错误地解析。 (案例由 Walter Landry 提供。)

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

the explanation is at 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.

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.)

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

失与倦" 2024-08-18 00:47:20

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

我想他们不赞成它,因为符号链接意味着折叠的路径不一定是等效的。如果c:\full\pathc:\rough的符号链接,那么c:\full\path\..将是<代码>c:\,而不是c:\full

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.

善良天后 2024-08-18 00:47:20

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

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.

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文