Wir verwenden boost :: filesystem in unserer Anwendung. Ich habe einen 'vollständigen' Pfad, der durch Zusammenfügen mehrerer Pfade erstellt wird:
#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();
}
Der endgültige Pfad wird gedruckt als:
c:\some\deep\application\folder\..\configuration\instance\..\instance\myfile.cfg
Dies ist ein gültiger Pfad, aber wenn ich ihn dem Benutzer anzeige, möchte ich, dass er normalisiert wird. (Hinweis: Ich bin mir nicht mal sicher, ob "normalisiert" das richtige Wort dafür ist). So was:
c:\some\deep\application\configuration\instance\myfile.cfg
Frühere Versionen von Boost hatten eine normalize()
-Funktion - sie scheint jedoch veraltet und entfernt worden zu sein (ohne Erklärung).
Gibt es einen Grund, warum ich das Makro BOOST_FILESYSTEM_NO_DEPRECATED
nicht verwenden sollte? Gibt es eine alternative Möglichkeit, dies mit der Boost-Dateisystembibliothek zu tun? Oder sollte ich Code schreiben, um den Pfad direkt als Zeichenfolge zu bearbeiten?
Sie können boost::filesystem::canonical
verwenden:
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 und höher bieten auch die boost::filesystem::read_symlink
-Funktion zum Auflösen symbolischer Links.
Wie in anderen Antworten erwähnt, können Sie nicht normalisieren, da das boost :: filesystem symbolischen Links nicht folgen kann. Sie können jedoch eine Funktion schreiben, die "so viel wie möglich" normalisiert (vorausgesetzt, "." Und ".." werden normalerweise behandelt), da boost die Möglichkeit bietet, festzustellen, ob eine Datei eine symbolische Verbindung ist oder nicht.
Das heißt, wenn das übergeordnete Element ".." ein symbolischer Link ist, müssen Sie es beibehalten. Andernfalls ist es wahrscheinlich, dass es gelöscht wird, und es ist wahrscheinlich immer sicher, "." Zu entfernen.
Es ist ähnlich wie bei der Bearbeitung der eigentlichen Saite, jedoch etwas eleganter.
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;
}
Mit Version 3 von boost::filesystem
können Sie auch versuchen, alle symbolischen Links mit einem Aufruf an canonical
zu entfernen. Dies ist nur für vorhandene Pfade möglich, sodass für eine Funktion, die auch für nicht vorhandene Pfade funktioniert, zwei Schritte erforderlich sind (getestet unter 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;
}
Ihre Beschwerden und/oder Wünsche zu canonical
wurden von Boost 1.60 [ 1 ] mit angesprochen
path lexically_normal(const path& p);
die Erklärung finden Sie unter http://www.boost.org/doc/libs/1_40_0/libs/filesystem/doc/design.htm :
Arbeiten Sie innerhalb der unten beschriebenen Realitäten.
Begründung: Dies ist kein Forschungsprojekt. Die Notwendigkeit ist etwas, das auf heutigen Plattformen funktioniert, einschließlich einiger eingebetteter Betriebssysteme mit eingeschränkten Dateisystemen. Aufgrund der Betonung der Portabilität wäre eine solche Bibliothek bei Standardisierung viel nützlicher. Das bedeutet, dass Sie mit einer viel breiteren Palette von Plattformen als Unix oder Windows und deren Klonen arbeiten können.
wo die "Realität" für das Entfernen von normalize
gilt:
Symbolische Links bewirken, dass einige Pfade kanonische und normale Formen annehmen, um verschiedene Dateien oder Verzeichnisse darzustellen. Wenn beispielsweise die Verzeichnishierarchie/a/b/c mit einem symbolischen Link in/a namens x auf b/c verweist, sollte sich unter POSIX-Pfadauflösungsregeln ein Pfad von "/ a/x/.." auflösen "/ a/b". Wenn "/ a/x/.." zuerst auf "/ a" normiert wurde, würde es falsch aufgelöst werden. (Fall von Walter Landry geliefert.)
die Bibliothek kann einen Pfad ohne Zugriff auf die zugrunde liegenden Dateisysteme nicht wirklich normalisieren. Dies macht die Operation a) unzuverlässig b) unvorhersehbar c) falsch d) alle oben genannten
Es ist immernoch da. Verwenden Sie es weiter.
Ich kann mir vorstellen, dass sie es veraltet haben, weil symbolische Links bedeuten, dass der eingestürzte Pfad nicht unbedingt gleichwertig ist. Wenn c:\full\path
ein Symlink zu c:\rough
wäre, wäre c:\full\path\..
c:\
und nicht c:\full
.
Da die "kanonische" Funktion nur mit vorhandenen Pfaden funktioniert, habe ich eine eigene Lösung entwickelt, die den Pfad in seine Teile aufteilt und jeden Teil mit dem nächsten vergleicht. Ich verwende das mit 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;
}
Um dies zu verwenden, finden Sie hier ein Beispiel, wie Sie es nennen:
std::cout<<NormalizePath("/home/../home/thatfile/")<<std::endl;