webentwicklung-frage-antwort-db.com.de

Wie "normalisiere" ich einen Pfadnamen mit boost :: filesystem?

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?

36
Mike Willekes

Boost v1.48 und höher

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.

Boost-Versionen vor v1.48

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;
}
30
Adam Bowen

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

Ihre Beschwerden und/oder Wünsche zu canonical wurden von Boost 1.60 [ 1 ] mit angesprochen

path lexically_normal(const path& p);
10

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

8
just somebody

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.

3
Jonathan Graehl

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;