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?

57
demandé sur Kiril Kirov 2010-12-13 19:05:17

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
142
répondu Nickolay Merkin 2014-02-17 11:00:40

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.

19
répondu Kos 2010-12-13 16:12:21

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  = "";
    }
}
13
répondu Sylvain Defresne 2015-01-22 01:35:19

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
8
répondu Yuchen Zhong 2017-04-12 15:30:36

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 va maintenant vous donner le nom de fichier
  4. faites un find_last_of pour ., et une sous-chaîne de chaque côté vous donnera name + extension.
3
répondu Nim 2010-12-13 16:10:58

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.

0
répondu svanschalkwyk 2014-11-16 16:29:07

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.

0
répondu Kemin Zhou 2017-04-07 22:39:10

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.

0
répondu Nicholas Mulianto 2018-10-02 04:10:28