J'ai une liste de fichiers stockés dans un .log
avec cette syntaxe:
c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg
Je veux extraire le nom et l'extension de ces fichiers. Pouvez-vous donner un exemple d'un moyen simple de faire cela?
Pour extraire un nom de fichier sans extension, utilisez boost :: filesystem :: path :: stem 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 (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 chemin.
BTW: Rappelez-vous aussi que pour utiliser le chemin comme
c:\foto\foto2003\shadow.gif
vous devez échapper le \
dans un littéral de chaîne:
const char* filename = "c:\\foto\\foto2003\\shadow.gif";
Ou utilisez plutôt /
:
const char* filename = "c:/foto/foto2003/shadow.gif";
Ceci s'applique uniquement à la spécification de chaînes littérales entre guillemets ""
. Le problème n'existe pas lorsque vous chargez des chemins d'accès à 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 votre nom de fichier est dans un std::string
, vous pouvez utiliser la méthode std::string::find_last_of
pour trouver le dernier séparateur.
Quelque chose comme ça:
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://fr.cppreference.com/w/cpp/filesystem
Comme suggéré par @RoiDanto, std::out
peut entourer la sortie de citations, par exemple pour
filename and extension: "file.ext"
Vous pouvez convertir std::filesystem::path
en std::string
par p.filename().string()
si c'est ce dont vous avez besoin, par exemple:
filename and extension: file.ext
Pas le code, mais voici l'idée:
std::string
à partir du flux d'entrée (std::ifstream
), chaque instance lue sera le chemin completfind_last_of
sur la chaîne pour le \
find_last_of
pour .
, et une sous-chaîne de chaque côté vous donnera le nom + l'extension.Les réponses de Nickolay Merkin et de 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 encapsule le nom du fichier entre guillemets. Les commentaires ne sont pas précis non plus.
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 en suspens puisque la chaîne sur laquelle la référence pointe a peut-être été détruite.
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 les fonctionnalités fournies par le système est que vous n'avez pas à installer boost ni à é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-direct de l'utiliser avec le code C++. Voici un exemple simple tiré 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 placer dans un bloc try/catch Au cas où quelque chose se passerait entre les deux appels.
J'utilise également cet extrait pour déterminer le caractère de barre oblique 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 on déploie constamment entre Linux/Windows.