Wie kann ich den Dateinamen und die Erweiterung aus einem Pfad in C ++ extrahieren?

75

Ich habe eine Liste von Dateien in a .login dieser Syntax gespeichert :

c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg

Ich möchte den Namen und die Erweiterung aus diesen Dateien extrahieren. Können Sie ein Beispiel für einen einfachen Weg geben, dies zu tun?

Octavian
quelle

Antworten:

175

Um einen Dateinamen ohne Erweiterung zu extrahieren, verwenden Sie boost :: filesystem :: path :: stem anstelle von hässlichem std :: string :: find_last_of (".")

boost::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension : " << p.filename() << std::endl; // file.ext
std::cout << "filename only          : " << p.stem() << std::endl;     // file
Nickolay Merkin
quelle
Einverstanden. Beantwortet die Frage am prägnantesten.
AndyUK
13
Tatsächlich ist p.filename () vom Typ path und wird bei impliziter Konvertierung von Anführungszeichen umgeben, sodass Sie Folgendes erhalten: Dateiname und Erweiterung: "file.ext" Möglicherweise möchten Sie stattdessen p.filename (). String ().
James Hirschorn
5
Mit C ++ 14 / C ++ 17 können Sie std::experimental::filesystemresp std::filesystem. Siehe Beitrag von Yuchen Zhong unten.
Roi Danton
Um die Erweiterung (mit Punkt) zu erhalten, verwenden Sie p.ext (). String ()
Yibo
Der Fragesteller wollte einen einfachen Weg. Das Hinzufügen eines Boosts zu einem Projekt allein für diese Funktionalität ist kein einfacher Weg. std :: filesystem ist der einfache Weg.
KKlouzal
28

Für C ++ 17 :

#include <filesystem>

std::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension: " << p.filename() << std::endl; // "file.ext"
std::cout << "filename only: " << p.stem() << std::endl;              // "file"

Referenz zum Dateisystem: http://en.cppreference.com/w/cpp/filesystem


Wie von @RoiDanto für die Ausgabeformatierung vorgeschlagen, std::outkann die Ausgabe mit Anführungszeichen umgeben sein, z.

filename and extension: "file.ext"

Sie können konvertieren std::filesystem::pathzu std::stringdurch , p.filename().string()wenn das ist , was Sie brauchen, zum Beispiel:

filename and extension: file.ext
Yuchen Zhong
quelle
Hey @RoiDanton, danke für die Bearbeitung! Ich habe gerade den Beispielcode im Referenzlink noch einmal überprüft. Es scheint nicht notwendig zu sein, den Rückgabetyp von std::filesystem::pathnach zu konvertieren , std::stringum ihn verwenden zu können std::cout. en.cppreference.com/w/cpp/filesystem/path/filename Wenn Sie jedoch etwas anderes denken, können Sie den Beitrag jederzeit kommentieren oder erneut bearbeiten.
Yuchen Zhong
Das stimmt, std::coutkann sich auf implizite Konvertierung verlassen. Da die Kommentare nach std::coutfile.ext und file jedoch entweder .string()zu den Kommentaren hinzugefügt werden müssen oder "file.ext" und "file" sein sollten. Mit Visual C ++ gibt es zwar keinen Unterschied (auch ohne string()die Ausgabe ohne Anführungszeichen), aber mit gcc 6.1 erfolgt die Ausgabe mit Anführungszeichen, wenn sie .string()weggelassen wird. Siehe coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
Roi Danton
@RoiDanton, hey, das ist ein interessanter Einblick. Ich werde den Beitrag erneut aktualisieren. Danke, dass du das geteilt hast!
Yuchen Zhong
19

Wenn Sie einen sicheren Weg suchen (dh zwischen Plattformen portabel und keine Annahmen auf den Pfad setzen), würde ich die Verwendung empfehlen boost::filesystem.

Es würde irgendwie so aussehen:

boost::filesystem::path my_path( filename );

Dann können Sie verschiedene Daten aus diesem Pfad extrahieren. Hier ist die Dokumentation des Pfadobjekts.


Übrigens: Denken Sie auch daran, um Pfad wie zu verwenden

c:\foto\foto2003\shadow.gif

Sie müssen das \in einem String-Literal entkommen :

const char* filename = "c:\\foto\\foto2003\\shadow.gif";

Oder verwenden Sie /stattdessen:

const char* filename = "c:/foto/foto2003/shadow.gif";

Dies gilt nur für die Angabe von Literalzeichenfolgen in ""Anführungszeichen. Das Problem tritt nicht auf, wenn Sie Pfade aus einer Datei laden.

Kos
quelle
2
+1 Auf jeden Fall der richtige Weg. Das Beispiel auf der Hauptseite bietet eine Möglichkeit zum Durchsuchen eines Verzeichnisses: Verwenden Sie die Methode path.extension (), um nach Protokollen zu suchen (siehe boost.org/doc/libs/1_36_0/libs/filesystem/doc/index.htm )
Tom
In der Tat ist dies in den meisten Fällen der richtige Weg, in einigen Fällen muss jedoch eine unerwünschte Abhängigkeit von einer externen Bibliothek hinzugefügt werden. Wenn Sie nur mit dem arbeiten möchten, was der C ++ - Standard bietet, empfehlen wir Ihnen, sich die C ++ - Regex anzusehen, in der Sie einen regulären Ausdruck definieren können, um das zu tun, was Sie möchten (viele Beispiele im Internet). Der Vorteil - kein Overhead aufgrund einiger zusätzlicher Abhängigkeiten. Dies lässt jedoch auch eine Frage offen: Ist Multiplattforming erforderlich? Boost kümmert sich um den Pfadstil, egal ob Sie Windows oder Linux verwenden. Mit Regex müssen Sie das selbst tun.
Rbaleksandar
17

Sie müssen Ihre Dateinamen aus der Datei in lesen std::string. Sie können den String-Extraktionsoperator von verwenden std::ostream. Sobald Sie Ihren Dateinamen in a haben std::string, können Sie die std::string::find_last_ofMethode verwenden, um das letzte Trennzeichen zu finden.

Etwas wie das:

std::ifstream input("file.log");
while (input)
{
    std::string path;
    input >> path;

    size_t sep = path.find_last_of("\\/");
    if (sep != std::string::npos)
        path = path.substr(sep + 1, path.size() - sep - 1);

    size_t dot = path.find_last_of(".");
    if (dot != std::string::npos)
    {
        std::string name = path.substr(0, dot);
        std::string ext  = path.substr(dot, path.size() - dot);
    }
    else
    {
        std::string name = path;
        std::string ext  = "";
    }
}
Sylvain Defresne
quelle
2
Willst du nicht klug sein, aber es sollte path.substr und nicht path.substring sein, oder?
Björn
3

Nicht der Code, aber hier ist die Idee:

  1. Lesen Sie a std::stringaus dem Eingabestream ( std::ifstream), jede gelesene Instanz ist der vollständige Pfad
  2. Mach ein find_last_ofauf der Schnur für die\
  3. Extrahieren Sie einen Teilstring von dieser Position bis zum Ende. Dadurch erhalten Sie jetzt den Dateinamen
  4. Tun Sie ein find_last_offür ., und ein Teilstring auf beiden Seiten gibt Ihnen Name + Erweiterung.
Nim
quelle
Und -1 für nicht tragbar :)
Kos
Warum die Abwahl? Wenn etwas mit dem, was ich gesagt habe, nicht stimmt, lass es mich wissen und ich werde es reparieren!
Nim
2
@Kos, na das ist hart! es entspricht den Wünschen des OP, die Datei basiert auf Windows und es gab keine Portabilitätsanforderungen!
Nim
Zumindest kann ein gültiger Windows-Pfad die Verzeichnisse auch durch trennen /. Und ich weiß nicht einmal, ob es mehr Einschränkungen bei den Pfadspezifikationen gibt, daher ist mein Denken einfach - wenn es eine gute Bibliothek gibt, die das tut, was ich will, sollte ich sie verwenden, weil sie mein Ziel wahrscheinlich besser erreichen wird, als ich kann. ;)
Kos
3
@Nim, aber gibt es dafür keine boost::insects::disperser<T>generische Vorlage? :)
Kos
0

Ich benutze dieses Snippet auch, um den passenden Schrägstrich zu bestimmen:

boost::filesystem::path slash("/");
    boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native();

und ersetzen Sie dann die Schrägstriche durch den bevorzugten Schrägstrich für das Betriebssystem. Nützlich, wenn ständig zwischen Linux / Windows bereitgestellt wird.

svanschalkwyk
quelle
0

Für Linux- oder Unix-Computer verfügt das Betriebssystem über zwei Funktionen, die sich mit Pfad- und Dateinamen befassen. Verwenden Sie den Basisnamen man 3, um weitere Informationen zu diesen Funktionen zu erhalten. Der Vorteil der Verwendung der vom System bereitgestellten Funktionen besteht darin, dass Sie keinen Boost installieren oder eigene Funktionen schreiben müssen.

#include <libgen.h>
       char *dirname(char *path);
       char *basename(char *path);

Beispielcode aus der Manpage:

   char *dirc, *basec, *bname, *dname;
           char *path = "/etc/passwd";

           dirc = strdup(path);
           basec = strdup(path);
           dname = dirname(dirc);
           bname = basename(basec);
           printf("dirname=%s, basename=%s\n", dname, bname);

Aufgrund des nicht konstanten Argumenttyps der Funktion basename () ist die Verwendung dieses C ++ - Codes etwas unkompliziert. Hier ist ein einfaches Beispiel aus meiner Codebasis:

string getFileStem(const string& filePath) const {
   char* buff = new char[filePath.size()+1];
   strcpy(buff, filePath.c_str());
   string tmp = string(basename(buff));
   string::size_type i = tmp.rfind('.');
   if (i != string::npos) {
      tmp = tmp.substr(0,i);
   }
   delete[] buff;
   return tmp;
}

Die Verwendung von new / delete ist kein guter Stil. Ich hätte es in einen Try / Catch-Block stecken können, falls zwischen den beiden Aufrufen etwas passiert wäre.

Kemin Zhou
quelle
0

Versuchen Sie den folgenden Trick, um den Dateinamen aus dem Pfad ohne Erweiterung in C ++ ohne externe Bibliotheken in C ++ zu extrahieren:

#include <iostream>
#include <string>

using std::string;

string getFileName(const string& s) {
char sep = '/';
#ifdef _WIN32
sep = '\\';
#endif
size_t i = s.rfind(sep, s.length());
if (i != string::npos) 
{
string filename = s.substr(i+1, s.length() - i);
size_t lastindex = filename.find_last_of("."); 
string rawname = filename.substr(0, lastindex); 
return(rawname);
}

return("");
}

int main(int argc, char** argv) {

string path = "/home/aymen/hello_world.cpp";
string ss = getFileName(path);
std::cout << "The file name is \"" << ss << "\"\n";
}
Aymen Alsaadi
quelle
-1

Die Antworten von Nickolay Merkin und Yuchen Zhong sind großartig, aber aus den Kommentaren geht hervor, dass sie nicht ganz korrekt sind.

Bei der impliziten Konvertierung in std :: string beim Drucken wird der Dateiname in Anführungszeichen gesetzt. Die Kommentare sind auch nicht korrekt.

path::filename()und path::stem()gibt ein neues Pfadobjekt zurück und path::string()gibt einen Verweis auf eine Zeichenfolge zurück. So etwas wie std::cout << file_path.filename().string() << "\n"könnte Probleme mit der baumelnden Referenz verursachen, da die Zeichenfolge, auf die die Referenz verweist, möglicherweise zerstört wurde.

Nicholas Mulianto
quelle