Comment puis-je extraire le nom de fichier et l'extension d'un chemin en C++
J'ai une liste de fichiers stockés dans un .log
dans cette syntaxe:
c:fotofoto2003shadow.gif
D:etcmom.jpg
Je veux extraire le nom et l'extension de ces fichiers. Pouvez-vous donner un exemple d'une façon simple de le faire?
8 réponses
Pour extraire un nom de fichier sans extension, utiliser boost::filesystem::chemin d'accès::souches au lieu de laid 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
Si vous voulez un moyen sûr (c'est-à-dire portable entre les plates-formes et ne pas mettre d'hypothèses sur le chemin), je vous recommande d'utiliser boost::filesystem
.
Cela ressemblerait en quelque sorte à ceci:
boost::filesystem::path my_path( filename );
Ensuite, vous pouvez extraire diverses données de ce chemin. Voici la documentation de l'objet path.
BTW: rappelez-vous aussi que pour utiliser path comme
c:\foto\foto2003\shadow.gif
Vous devez échapper le \
dans une chaîne littérale:
const char* filename = "c:\\foto\\foto2003\\shadow.gif";
Ou utilisez /
à la place:
const char* filename = "c:/foto/foto2003/shadow.gif";
Ce s'applique uniquement à la spécification de chaînes littérales dans les guillemets ""
, le problème n'existe pas lorsque vous chargez des chemins à partir d'un fichier.
Vous devrez lire vos noms de fichiers à partir du fichier dans std::string
. Vous pouvez utiliser l'opérateur d'extraction de chaîne de std::ostream
. Une fois que vous avez votre nom de fichier dans un std::string
, vous pouvez utiliser la méthode std::string::find_last_of
pour trouver le dernier séparateur.
Quelque Chose comme ceci:
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 = "";
}
}
Pour 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"
Référence sur le système de fichiers: http://en.cppreference.com/w/cpp/filesystem
Comme suggéré par @RoiDanto , pour la mise en forme de sortie, {[3] } peut entourer la sortie de citations, par exemple:
filename and extension: "file.ext"
, Vous pouvez convertir std::filesystem::path
à std::string
par p.filename().string()
, si c'est ce que vous avez besoin, par exemple:
filename and extension: file.ext
Pas le code, mais voici l'idée:
- lire un
std::string
à partir du flux d'entrée (std::ifstream
), chaque instance lue sera le chemin complet - faites un
find_last_of
sur la chaîne pour le\
- extraire une sous-chaîne de cette position à la fin, cela va maintenant vous donner le nom de fichier
- faites un
find_last_of
pour.
, et une sous-chaîne de chaque côté vous donnera name + extension.
J'utilise également cet extrait pour déterminer le caractère slash approprié:
boost::filesystem::path slash("/");
boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native();
Puis remplacez les barres obliques par la barre oblique préférée pour le système d'exploitation. Utile si l'on se déploie constamment entre Linux / Windows.
Pour les machines linux ou unix, le système d'exploitation a deux fonctions traitant des noms de chemin et de fichier. utilisez man 3 basename pour obtenir plus d'informations sur ces fonctions. L'avantage d'utiliser la fonctionnalité fournie par le système est que vous n'avez pas besoin d'installer boost ou d'écrire vos propres fonctions.
#include <libgen.h>
char *dirname(char *path);
char *basename(char *path);
Exemple de code de la page de manuel:
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);
En raison du type d'argument non const de la fonction basename (), il est un peu Non simple en utilisant ceci dans C++ code. Voici un exemple simple de ma base de code:
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;
}
L'utilisation de new/delete n'est pas un bon style. J'aurais pu le mettre dans un essai / catch bloquer au cas où quelque chose se produirait entre les deux appels.
Les réponses de Nickolay Merkin et Yuchen Zhong sont excellentes, mais d'après les commentaires, vous pouvez voir que ce n'est pas tout à fait exact.
La conversion implicite en std::string lors de l'impression enveloppera le nom du fichier dans des citations. Les commentaires ne sont pas fiables.
path::filename()
et path::stem()
renvoie un nouvel objet de chemin et path::string()
renvoie une référence à une chaîne. Ainsi, quelque chose comme std::cout << file_path.filename().string() << "\n"
pourrait causer des problèmes avec la référence pendante puisque la chaîne vers laquelle la référence pointe pourrait ont été détruits.