web-dev-qa-db-fra.com

Comment puis-je extraire le nom de fichier et l'extension à partir d'un chemin en C++

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?

60
Octavian

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
148
Nickolay Merkin

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.

19
Kos

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  = "";
    }
}
14
Sylvain Defresne

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
11
Yuchen Zhong

Pas le code, mais voici l'idée:

  1. Lire un std::string à partir du flux d'entrée (std::ifstream), chaque instance lue sera le chemin complet
  2. Faites un find_last_of sur la chaîne pour le \
  3. Extraire une sous-chaîne de cette position à la fin, cela vous donnera maintenant le nom du fichier
  4. Faites un find_last_of pour ., et une sous-chaîne de chaque côté vous donnera le nom + l'extension.
3
Nim

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.

0

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. 

0
Kemin Zhou

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.

0
svanschalkwyk