Ich habe eine Liste von Dateien, die in einem .log
in dieser Syntax gespeichert sind:
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 nennen?
Um einen Dateinamen ohne Erweiterung zu extrahieren, verwenden Sie boost :: filesystem :: path :: stem anstatt hässlichen 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
Wenn Sie einen sicheren Weg wünschen (d. H. Portabel zwischen Plattformen und keine Annahmen in den Pfad setzen), würde ich empfehlen, boost::filesystem
zu verwenden.
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.
BTW: Denken Sie auch daran, um pfadähnlich zu verwenden
c:\foto\foto2003\shadow.gif
sie müssen den \
in einem String-Literal entziehen:
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 besteht nicht, wenn Sie Pfade aus einer Datei laden.
Sie müssen Ihre Dateinamen aus der Datei in std::string
lesen. Sie können den Zeichenfolgen-Extraktionsoperator von std::ostream
verwenden. Wenn Sie Ihren Dateinamen in einem std::string
gespeichert haben, können Sie die std::string::find_last_of
-Methode 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 = "";
}
}
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 vorgeschlagen, kann std::out
für die Ausgabeformatierung Zitate enthalten, z. B .:
filename and extension: "file.ext"
Sie können std::filesystem::path
in std::string
durch p.filename().string()
konvertieren, wenn Sie dies benötigen, z.
filename and extension: file.ext
Nicht der Code, aber hier ist die Idee:
std::string
aus dem Eingabestrom (std::ifstream
). Jede gelesene Instanz ist der vollständige Pfadfind_last_of
für die Zeichenfolge für den \
aus.find_last_of
für .
aus, und eine Unterzeichenfolge auf beiden Seiten gibt Ihnen Namen + Erweiterung.Die Antworten von Nickolay Merkin und Yuchen Zhong sind großartig, aber den Kommentaren ist zu entnehmen, dass sie nicht ganz genau sind.
Bei der impliziten Konvertierung in std :: string wird beim Drucken der Dateiname in Anführungszeichen gesetzt. Die Kommentare sind auch nicht korrekt.
path::filename()
und path::stem()
geben ein neues Pfadobjekt und path::string()
eine Referenz auf einen String zurück. Daher könnte etwas wie std::cout << file_path.filename().string() << "\n"
Probleme mit der Referenz-Referenz verursachen, da die Zeichenfolge, auf die die Referenz verweist, möglicherweise zerstört wurde.
Für Linux- oder Unix-Maschinen verfügt das Betriebssystem über zwei Funktionen, die sich auf Pfad- und Dateinamen beziehen. Verwenden Sie man 3 basename, um weitere Informationen zu diesen Funktionen zu erhalten .. Der Vorteil der Verwendung der vom System bereitgestellten Funktionalität besteht darin, dass Sie keinen Boost installieren oder Ihre eigenen 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 Argumenttyps non-const der Funktion basename () ist es etwas unkompliziert, diesen Code innerhalb von C++ zu verwenden. Hier ist ein einfaches Beispiel aus meiner Code-Basis:
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 Neu/Löschen ist kein guter Stil. Ich hätte es in einen try/catch -Block legen können, falls zwischen den beiden Aufrufen etwas passiert ist.
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.