C++ 如何使用 boost::filesystem“规范化”路径名?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1746136/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I "normalize" a pathname using boost::filesystem?
提问by Mike Willekes
We are using boost::filesystem in our application. I have a 'full' path that is constructed by concatenating several paths together:
我们在我们的应用程序中使用 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();
}
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 文件系统库来做到这一点?或者我应该编写代码来直接将路径作为字符串进行操作?
采纳答案by Adam Bowen
Boost v1.48 and above
Boost v1.48 及以上
You can use boost::filesystem::canonical
:
您可以使用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
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.
v1.48 及以上版本还提供了boost::filesystem::read_symlink
解析符号链接的功能。
Boost versions prior to v1.48
v1.48 之前的 Boost 版本
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::filesystem 无法遵循符号链接。但是,您可以编写一个“尽可能多地”规范化的函数(假设“.”和“..”被正常处理),因为 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;
}
回答by jarzec
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):
在版本 3 中,boost::filesystem
您还可以尝试通过调用canonical
. 这只能对现有路径完成,因此一个也适用于不存在路径的函数需要两个步骤(在 MacOS Lion 上测试并为 Windows 更新,感谢@void.pointer 的评论):
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();
}
回答by Alexander Shukaev
回答by just somebody
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:
适用于去除的“现实”normalize
是:
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 中的符号链接名为 x 指向 b/c,然后在 POSIX 路径名解析规则下,路径“/a/x/..”应解析为“/a/b”。如果首先将“/a/x/..”规范化为“/a”,它将无法正确解析。(案例由 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
在没有访问底层文件系统的情况下,库无法真正规范化路径,这使得操作 a) 不可靠 b) 不可预测 c) 错误 d) 以上所有
回答by Jonathan Graehl
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:\full
。
回答by The Quantum Physicist
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;