Hoe kan ik de bestandsnaam en extensie uit een pad in C++ halen

Ik heb een lijst met bestanden die zijn opgeslagen in een .login deze syntaxis:

c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg

Ik wil de naam en de extensie uit deze bestanden halen. Kun je een voorbeeld geven van een eenvoudige manier om dit te doen?


Antwoord 1, autoriteit 100%

Om een ​​bestandsnaam zonder extensie te extraheren, gebruik je boost::filesystem::path::stemin plaats van lelijke 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

Antwoord 2, autoriteit 21%

Voor 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"

Referentie over bestandssysteem: http://en.cppreference.com/w/cpp/filesystem


Zoals gesuggereerd door @RoiDanto, kan std::outvoor de uitvoeropmaak de uitvoer tussen aanhalingstekens plaatsen, bijvoorbeeld:

filename and extension: "file.ext"

Je kunt std::filesystem::pathnaar std::stringconverteren door p.filename().string()als dat is wat je nodig hebt, bijvoorbeeld:

filename and extension: file.ext

Antwoord 3, autoriteit 9%

Als je een veilige manier wilt (d.w.z. draagbaar tussen platforms en geen aannames op het pad plaatst), raad ik aan om boost::filesystemte gebruiken.

Het zou er ongeveer zo uitzien:

boost::filesystem::path my_path( filename );

Vervolgens kunt u verschillende gegevens uit dit pad halen. Hier is de documentatie van het path-object.


BTW: onthoud ook dat om het pad te gebruiken zoals

c:\foto\foto2003\shadow.gif

je moet de \escapen in een letterlijke tekenreeks:

const char* filename = "c:\\foto\\foto2003\\shadow.gif";

Of gebruik in plaats daarvan /:

const char* filename = "c:/foto/foto2003/shadow.gif";

Dit is alleen van toepassing op het specificeren van letterlijke tekenreeksen tussen ""aanhalingstekens, het probleem bestaat niet wanneer u paden uit een bestand laadt.


Antwoord 4, autoriteit 9%

Je moet je bestandsnamen lezen uit het bestand in std::string. U kunt de tekenreeksextractie-operator van std::ostreamgebruiken. Zodra je bestandsnaam in een std::stringstaat, kun je de std::string::find_last_ofmethode gebruiken om het laatste scheidingsteken te vinden.

Zoiets:

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

Antwoord 5, autoriteit 2%

Niet de code, maar hier is het idee:

  1. Lees een std::stringuit de invoerstroom (std::ifstream), elke gelezen instantie is het volledige pad
  2. Voer een find_last_ofuit op de string voor de \
  3. Extract een substring van deze positie tot het einde, dit geeft je nu de bestandsnaam
  4. Doe een find_last_ofvoor ., en een substring aan weerszijden geeft je naam + extensie.

Antwoord 6

De volgende truc om de bestandsnaam te extraheren uit een bestandspad zonder extensie in c++ (geen externe bibliotheken vereist):

#include <iostream>
#include <string>
using std::string;
string getFileName(const string& s) {
char sep = '/';
#ifdef _WIN32
sep = '\\';
#endif
size_t i = s.rfind(sep, s.length());
if (i != string::npos) 
{
string filename = s.substr(i+1, s.length() - i);
size_t lastindex = filename.find_last_of("."); 
string rawname = filename.substr(0, lastindex); 
return(rawname);
}
return("");
}
int main(int argc, char** argv) {
string path = "/home/aymen/hello_world.cpp";
string ss = getFileName(path);
std::cout << "The file name is \"" << ss << "\"\n";
}

Antwoord 7

Ik gebruik dit fragment ook om de juiste schuine streep te bepalen:

boost::filesystem::path slash("/");
    boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native();

en vervang de schuine streep door de gewenste schuine streep voor het besturingssysteem. Handig als men constant aan het implementeren is tussen Linux/Windows.


Antwoord 8

Voor linux- of unix-machines heeft het besturingssysteem twee functies voor pad- en bestandsnamen. gebruik man 3 basename om meer informatie over deze functies te krijgen.
Het voordeel van het gebruik van de door het systeem geleverde functionaliteit is dat u geen boost hoeft te installeren of uw eigen functies hoeft te schrijven.

#include <libgen.h>
       char *dirname(char *path);
       char *basename(char *path);

Voorbeeldcode van de man-pagina:

  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);

Vanwege het niet-const-argumenttype van de functie basename() is het een beetje niet eenvoudig om dit in C++-code te gebruiken. Hier is een eenvoudig voorbeeld uit mijn codebasis:

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;
}

Het gebruik van nieuw/verwijder is geen goede stijl. Ik had het in een try/catch kunnen zetten
blokkeren voor het geval er iets tussen de twee gesprekken is gebeurd.


Antwoord 9

De antwoorden van Nickolay Merkin en Yuchen Zhong zijn geweldig, maar uit de opmerkingen kun je zien dat het niet helemaal juist is.

De impliciete conversie naar std::string bij het afdrukken plaatst de bestandsnaam tussen aanhalingstekens. De opmerkingen zijn ook niet correct.

path::filename()en path::stem()retourneert een nieuw path-object en path::string()retourneert een verwijzing naar een string. Dus zoiets als std::cout << file_path.filename().string() << "\n"kan problemen veroorzaken met bungelende referentie, aangezien de string waarnaar de referentie verwijst mogelijk is vernietigd.

Other episodes