So erhalten Sie die Dateierweiterung von einer Zeichenfolge in C ++

79

"filename.conf"Wie überprüfe ich anhand einer Zeichenfolge den Erweiterungsteil?

Ich brauche eine plattformübergreifende Lösung.

JeffV
quelle

Antworten:

34

Sie müssen sicherstellen, dass Sie sich um Dateinamen mit mehr als einem Punkt kümmern. Beispiel: c:\.directoryname\file.name.with.too.many.dots.extwürde von strchroder nicht richtig behandeltfind.

Mein Favorit wäre die Boost-Dateisystembibliothek mit einer Erweiterungsfunktion (Pfad)

Thomas Bonini
quelle
12
Ihr Verzeichnisname kann jedoch leicht durch umgekehrte Suche behandelt werden :).
17 von 26
29
Meiner persönlichen Meinung nach sollten Boost-Lösungen nicht als Antworten auf C ++ - Probleme aufgeführt werden. Eine externe Bibliothek für etwas so Einfaches zu benötigen, scheint ein bisschen albern.
Sumpf
4
@marsh: Dennoch hat das so einfache Problem seine Sonderfälle, insbesondere beim Umgang mit Dateisystemen - ein Konzept, für das fast jedes wichtige (und nicht so wichtige) Betriebssystem seine eigene Interpretation hat. Betrachten Sie beispielsweise versteckte Linux-Dateien (`/home/oren/.conf ') oder den von @Torlack erwähnten Fall . @ 17 von 26, der Versuch, nur Ihren Benutzernamen zu erwähnen, sollte die Probleme hervorheben, die sich aus einer zu starken Vereinfachung der Verwendung von Freiform-Namen ergeben können;)
Oren S
@OrenS Trotzdem sollte die Boost-Lösung niemals als Antwort auf eine Frage akzeptiert werden, bei der nicht gefragt wird, wie dies mit Boost geschehen soll. Es ist irreführend.
Silidrone
@MuhamedCicak ... nun, eine tragbare Lösung für othervise beinhaltet einen langen Code, der die Codierung von Dateinamen berücksichtigt oder / und andere Bibliotheken verwendet (ich vermute, Boost implementiert sie nicht von Grund auf neu, sondern verwendet stattdessen andere Pakete oder APIs, bei denen möglich). Beachten Sie, dass es bei einem halben Dutzend
Swift - Friday Pie
154

Ist das eine zu einfache Lösung?

#include <iostream>
#include <string>

int main()
{
  std::string fn = "filename.conf";
  if(fn.substr(fn.find_last_of(".") + 1) == "conf") {
    std::cout << "Yes..." << std::endl;
  } else {
    std::cout << "No..." << std::endl;
  }
}
Brian Newman
quelle
12
@Was passiert, wenn der Dateiname keine Erweiterung hat und der vorherige Ordner hat. in seinem Namen?
Mircea Ispas
4
Ich beantworte die Frage. Dies gibt "filename.conf" an, nicht Ihre Hypothese.
Brian Newman
5
Nach dieser Logik könnte man einfach sagen, dass es überhaupt return "Yes...";keine Prüfung gibt - dies impliziert, dass die Lösung für andere Eingaben funktionieren sollte. Als weiteres Zählerbeispiel würde eine Datei mit dem Namen "conf" ohne Erweiterung angesichts der obigen Angaben auch "Ja ..." zurückgeben.
Rollie
4
Warnung an andere: Dies ist eine zu einfache Lösung, um im Produktionscode verwendet zu werden, mit Ausnahme enger und spezifischer Projekte, die nicht mit einer Vielzahl realer Endbenutzerszenarien umgehen müssen. Das Parsen und Behandeln von Dateinamen ist nicht trivial. Ich persönlich benutze fast immer boost::filesystem, was trivial zu benutzen ist, aber die notwendige Unterstützung bietet. Siehe boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm
Dan Nissenbaum
1
Die Erweiterung std :: filesystem :: path :: ist jetzt Teil des Standards. Überprüfen Sie beispielsweise, ob Roi Danton unten antwortet.
Yves
42

Der beste Weg ist, keinen Code zu schreiben, der dies tut, sondern vorhandene Methoden aufzurufen. In Windows ist die PathFindExtension- Methode wahrscheinlich die einfachste.

Warum sollten Sie nicht Ihre eigenen schreiben?

Nehmen Sie das Beispiel strrchr. Was passiert, wenn Sie diese Methode für die folgende Zeichenfolge "c: \ Programme \ AppleGate.Net \ readme" verwenden? Ist ".Net \ readme" die Erweiterung? Es ist einfach, etwas zu schreiben, das für einige Beispielfälle funktioniert, aber es kann viel schwieriger sein, etwas zu schreiben, das für alle Fälle funktioniert.

Torlack
quelle
3
+1 Kein neuer Code zu schreiben ist oft die beste Antwort! Die C # -Version war genau das, wonach ich gerade gesucht habe, aber Ihre Antwort hat mich dorthin gebracht. msdn.microsoft.com/en-us/library/…
Tom Resing
Diese Funktion (unter Windows 7) verarbeitet "file.i i" nicht ordnungsgemäß. Ja, dies ist gültig, beachten Sie das Leerzeichen.
pcunite
Er hat nach dem Abrufen der Erweiterung aus einer Datei gefragt, nicht nach einem vollständigen Pfad. Darüber hinaus wäre eine Windows-API-Funktion keine gute Antwort. Dies ist absolut keine Antwort, sondern ein Kommentar.
Didac Perez Parera
4
-1 für die Bereitstellung einer plattformspezifischen Lösung, wenn vom OP eine tragbare Lösung angefordert wurde.
jb
+1 Von mir. Diese Frage ist die erste, die auftaucht, wenn Sie "mfc get file extension" googeln, und Ihre Frage ist die einfachste, die funktioniert.
Eternal21
32

Angenommen, Sie haben Zugriff auf STL:

std::string filename("filename.conf");
std::string::size_type idx;

idx = filename.rfind('.');

if(idx != std::string::npos)
{
    std::string extension = filename.substr(idx+1);
}
else
{
    // No extension found
}

Bearbeiten: Dies ist eine plattformübergreifende Lösung, da Sie die Plattform nicht erwähnt haben. Wenn Sie speziell mit Windows arbeiten, möchten Sie die Windows-spezifischen Funktionen nutzen, die von anderen im Thread erwähnt werden.

17 von 26
quelle
6
+1, dies ist die einfachste Lösung für den Fall, dass Sie eine Datei in einer Zeichenfolge und keinen Pfad haben!
Thomas Bonini
25

Jemand anderes erwähnte Boost, aber ich wollte nur den eigentlichen Code hinzufügen, um dies zu tun:

#include <boost/filesystem.hpp>
using std::string;
string texture         = foo->GetTextureFilename();
string file_extension  = boost::filesystem::extension(texture);
cout << "attempting load texture named " << texture
     << "    whose extensions seems to be " 
     << file_extension << endl;
// Use JPEG or PNG loader function, or report invalid extension
Peter Karasev
quelle
20

Tatsächlich kann die STL dies ohne viel Code tun. Ich rate Ihnen, etwas über die STL zu lernen, da Sie damit einige ausgefallene Dinge tun können. Jedenfalls ist dies das, was ich verwende.

std::string GetFileExtension(const std::string& FileName)
{
    if(FileName.find_last_of(".") != std::string::npos)
        return FileName.substr(FileName.find_last_of(".")+1);
    return "";
}

Diese Lösung gibt die Erweiterung auch bei Zeichenfolgen wie "this.abcdesmp3" immer zurück, wenn sie die zurückgegebene Erweiterung nicht finden kann.

Graphitemaster
quelle
13

Mit C ++ 17 und seiner std::filesystem::path::extension(die Bibliothek ist der Nachfolger von boost :: filesystem) würden Sie Ihre Aussage ausdrucksvoller machen als z std::string.

#include <iostream>
#include <filesystem> // C++17
namespace fs = std::filesystem;

int main()
{
    fs::path filePath = "my/path/to/myFile.conf";
    if (filePath.extension() == ".conf") // Heed the dot.
    {
        std::cout << filePath.stem() << " is a valid type."; // Output: "myFile is a valid type."
    }
    else
    {
        std::cout << filePath.filename() << " is an invalid type."; // Output: e.g. "myFile.cfg is an invalid type"
    }
}

Siehe auch std :: filesystem :: path :: stem , std :: filesystem :: path :: filename .

Roi Danton
quelle
7

Eigentlich ist der einfachste Weg

char* ext;
ext = strrchr(filename,'.') 

Eine Sache, an die Sie sich erinnern sollten: Wenn '.'der Dateiname nicht vorhanden ist, ist ext NULL.

Qiu
quelle
4
Dies wäre keine perfekte Lösung für versteckte UNIX-Dateien, die mit dot beginnen
Mark Kahn
sollte es const char * ext sein?
Vlad
4

Ich bin heute selbst auf diese Frage gestoßen, obwohl ich bereits einen funktionierenden Code hatte, stellte ich fest, dass er in einigen Fällen nicht funktionieren würde.

Während einige Leute bereits vorgeschlagen haben, einige externe Bibliotheken zu verwenden, schreibe ich lieber meinen eigenen Code für Lernzwecke.

Einige Antworten enthielten die Methode, die ich zuerst verwendet hatte (auf der Suche nach dem letzten "."), Aber ich erinnerte mich, dass versteckte Dateien / Ordner unter Linux mit "." Beginnen. Wenn die Dateidatei ausgeblendet ist und keine Erweiterung hat, wird der gesamte Dateiname als Erweiterung verwendet. Um dies zu vermeiden, habe ich diesen Code geschrieben:

bool getFileExtension(const char * dir_separator, const std::string & file, std::string & ext)
{
    std::size_t ext_pos = file.rfind(".");
    std::size_t dir_pos = file.rfind(dir_separator);

    if(ext_pos>dir_pos+1)
    {
        ext.append(file.begin()+ext_pos,file.end());
        return true;
    }

    return false;
}

Ich habe dies nicht vollständig getestet, aber ich denke, dass es funktionieren sollte.

Serengeor
quelle
3

Die Verwendung von find / rfind von std :: string löst DIESES Problem. Wenn Sie jedoch viel mit Pfaden arbeiten, sollten Sie sich boost :: filesystem :: path ansehen, da dies Ihren Code viel sauberer macht als das Fummeln mit rohen String-Indizes / Iteratoren.

Ich schlage Boost vor, da es sich um eine hochwertige, gut getestete (Open Source und kommerziell) kostenlose und vollständig portable Bibliothek handelt.

KristianR
quelle
3

Für Zeichenfolgen vom Typ char array können Sie Folgendes verwenden:

#include <ctype.h>
#include <string.h>

int main()
{
    char filename[] = "apples.bmp";
    char extension[] = ".jpeg";

    if(compare_extension(filename, extension) == true)
    {
        // .....
    } else {
        // .....
    }

    return 0;
}

bool compare_extension(char *filename, char *extension)
{
    /* Sanity checks */

    if(filename == NULL || extension == NULL)
        return false;

    if(strlen(filename) == 0 || strlen(extension) == 0)
        return false;

    if(strchr(filename, '.') == NULL || strchr(extension, '.') == NULL)
        return false;

    /* Iterate backwards through respective strings and compare each char one at a time */

    for(int i = 0; i < strlen(filename); i++)
    {
        if(tolower(filename[strlen(filename) - i - 1]) == tolower(extension[strlen(extension) - i - 1]))
        {
            if(i == strlen(extension) - 1)
                return true;
        } else
            break;
    }

    return false;
}

Kann neben Dateinamen auch Dateipfade verarbeiten. Funktioniert sowohl mit C als auch mit C ++. Und plattformübergreifend.

delaccount992
quelle
Sie können die Anzahl der Bedingungen verringern. Verwendung strlen(extension)in einwandfreiem forZustand. Wenn die Zeichen nicht übereinstimmen, geben Sie false zurück. Außerhalb der forSchleife return true.
LRDPRDX
3

Gute Antworten, aber ich sehe, dass die meisten von ihnen einige Probleme haben: Zunächst denke ich, dass eine gute Antwort für vollständige Dateinamen mit ihren Pfadüberschriften funktionieren sollte, auch für Linux oder Windows oder wie erwähnt plattformübergreifend. Für die meisten Antworten; Dateinamen ohne Erweiterung, aber mit einem Pfad mit einem Ordnernamen einschließlich Punkt. Die Funktion gibt nicht die richtige Erweiterung zurück: Beispiele für einige Testfälle könnten sein:

    const char filename1 = {"C:\\init.d\\doc"}; // => No extention
    const char filename2 = {"..\\doc"}; //relative path name => No extention
    const char filename3 = {""}; //emputy file name => No extention
    const char filename4 = {"testing"}; //only single name => No extention
    const char filename5 = {"tested/k.doc"}; // normal file name => doc
    const char filename6 = {".."}; // parent folder => No extention
    const char filename7 = {"/"}; // linux root => No extention
    const char filename8 = {"/bin/test.d.config/lx.wize.str"}; // ordinary path! => str

Der Vorschlag " brian newman " schlägt für Dateiname1 und Dateiname4 fehl. und die meisten anderen Antworten, die auf Reverse Find basieren, schlagen für Dateiname1 fehl. Ich schlage vor, die folgende Methode in Ihre Quelle aufzunehmen: Diese Funktion gibt den Index des ersten Zeichens der Erweiterung oder die Länge der angegebenen Zeichenfolge zurück, wenn sie nicht gefunden wird.

size_t find_ext_idx(const char* fileName)
{
    size_t len = strlen(fileName);
    size_t idx = len-1;
    for(size_t i = 0; *(fileName+i); i++) {
        if (*(fileName+i) == '.') {
            idx = i;
        } else if (*(fileName + i) == '/' || *(fileName + i) == '\\') {
            idx = len - 1;
        }
    }
    return idx+1;
}

Sie können den obigen Code in Ihrer C ++ - Anwendung wie folgt verwenden:

std::string get_file_ext(const char* fileName)
{
    return std::string(fileName).substr(find_ext_idx(fileName));
}

Der letzte Punkt in einigen Fällen ist, dass ein Ordner dem Dateinamen als Argument zugewiesen wird und einen Punkt im Ordnernamen enthält. Die Funktion gibt die Punktverfolgung des Ordners zurück. Überprüfen Sie daher besser zuerst, ob der angegebene Name ein Dateiname und kein Ordnername ist.

AMCoded
quelle
3

Eine NET / CLI-Version mit System :: String

   System::String^ GetFileExtension(System::String^ FileName)
   {
       int Ext=FileName->LastIndexOf('.');
       if( Ext != -1 )
           return FileName->Substring(Ext+1);
       return "";
   }
Leopoldo Sanczyk
quelle
Dies ist nicht Visual C ++, sondern .NET / CLI .
Victor
1
@ Victor Ich habe die Antwort bearbeitet. Danke für die Klarstellung.
Leopoldo Sanczyk
3

Ich würde mit boost::filesystem::extension( std::filesystem::path::extensionmit C ++ 17) gehen, aber wenn Sie Boost nicht verwenden können und nur die Erweiterung überprüfen müssen, ist eine einfache Lösung:

bool ends_with(const std::string &filename, const std::string &ext)
{
  return ext.length() <= filename.length() &&
         std::equal(ext.rbegin(), ext.rend(), filename.rbegin());
}

if (ends_with(filename, ".conf"))
{ /* ... */ }
Manlio
quelle
3
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Dies ist nur Windows (Platform SDK)

Erdferkel
quelle
2

Dies ist eine Lösung, die ich mir ausgedacht habe. Dann bemerkte ich, dass es ähnlich ist wie das, was @serengeor gepostet hat.

Es funktioniert mit std::stringund find_last_of, aber die Grundidee funktioniert auch, wenn sie geändert wird, um charArrays und zu verwenden strrchr. Es verarbeitet versteckte Dateien und zusätzliche Punkte, die das aktuelle Verzeichnis darstellen. Es ist plattformunabhängig.

string PathGetExtension( string const & path )
{
  string ext;

  // Find the last dot, if any.
  size_t dotIdx = path.find_last_of( "." );
  if ( dotIdx != string::npos )
  {
    // Find the last directory separator, if any.
    size_t dirSepIdx = path.find_last_of( "/\\" );

    // If the dot is at the beginning of the file name, do not treat it as a file extension.
    // e.g., a hidden file:  ".alpha".
    // This test also incidentally avoids a dot that is really a current directory indicator.
    // e.g.:  "alpha/./bravo"
    if ( dotIdx > dirSepIdx + 1 )
    {
      ext = path.substr( dotIdx );
    }
  }

  return ext;
}

Gerätetest:

int TestPathGetExtension( void )
{
  int errCount = 0;

  string tests[][2] = 
  {
    { "/alpha/bravo.txt", ".txt" },
    { "/alpha/.bravo", "" },
    { ".alpha", "" },
    { "./alpha.txt", ".txt" },
    { "alpha/./bravo", "" },
    { "alpha/./bravo.txt", ".txt" },
    { "./alpha", "" },
    { "c:\\alpha\\bravo.net\\charlie.txt", ".txt" },
  };

  int n = sizeof( tests ) / sizeof( tests[0] );

  for ( int i = 0; i < n; ++i )
  {
    string ext = PathGetExtension( tests[i][0] );
    if ( ext != tests[i][1] )
    {
      ++errCount;
    }
  }

  return errCount;
}
Mike Finch
quelle
2

Ich benutze diese beiden Funktionen, um die Erweiterung und den Dateinamen ohne Erweiterung zu erhalten :

std::string fileExtension(std::string file){

    std::size_t found = file.find_last_of(".");
    return file.substr(found+1);

}

std::string fileNameWithoutExtension(std::string file){

    std::size_t found = file.find_last_of(".");
    return file.substr(0,found);    
}

Und diese regexAnsätze für bestimmte zusätzliche Anforderungen:

std::string fileExtension(std::string file){

    std::regex re(".*[^\\.]+\\.([^\\.]+$)");
    std::smatch result;
    if(std::regex_match(file,result,re))return result[1];
    else return "";

}

std::string fileNameWithoutExtension(std::string file){

    std::regex re("(.*[^\\.]+)\\.[^\\.]+$");
    std::smatch result;
    if(std::regex_match(file,result,re))return result[1];
    else return file;

}

Zusätzliche Anforderungen, die von der Regex-Methode erfüllt werden:

  1. Wenn Dateinamen wie ist .configoder so etwas, Erweiterung wird ein leerer String und sein Dateinamen ohne Erweiterung sein wird .config.
  2. Wenn der Dateiname keine Erweiterung hat, ist die Erweiterung eine leere Zeichenfolge, der Dateiname ohne Erweiterung ist der Dateiname unverändert.

BEARBEITEN:

Die zusätzlichen Anforderungen können auch durch Folgendes erfüllt werden:

std::string fileExtension(const std::string& file){
    std::string::size_type pos=file.find_last_of('.');
    if(pos!=std::string::npos&&pos!=0)return file.substr(pos+1);
    else return "";
}


std::string fileNameWithoutExtension(const std::string& file){
    std::string::size_type pos=file.find_last_of('.');
    if(pos!=std::string::npos&&pos!=0)return file.substr(0,pos);
    else return file;
}

Hinweis:

Übergeben Sie in den obigen Funktionen nur die Dateinamen (nicht den Pfad).

Jahid
quelle
1

Versuchen Sie, strstr zu verwenden

char* lastSlash;
lastSlash = strstr(filename, ".");
Maadiah
quelle
1

Oder Sie können dies verwenden:

    char *ExtractFileExt(char *FileName)
    {
        std::string s = FileName;
        int Len = s.length();
        while(TRUE)
        {
            if(FileName[Len] != '.')
                Len--;
            else
            {
                char *Ext = new char[s.length()-Len+1];
                for(int a=0; a<s.length()-Len; a++)
                    Ext[a] = FileName[s.length()-(s.length()-Len)+a];
                Ext[s.length()-Len] = '\0';
                return Ext;
            }
        }
    }

Dieser Code ist plattformübergreifend

Suche
quelle
1

Wenn Sie Qt - Bibliothek verwenden, können Sie einen Versuch geben QFileInfo ‚s - Suffix ()

Mark Kahn
quelle
2
Was hat Qt mit dieser Frage zu tun? Warum sollte eine große Abhängigkeit von Drittanbietern für eine einfache Zeichenfolgenmanipulation eingeführt werden? Wenn Sie diesen Weg gehen, warum nicht einfach Boost verwenden?
derpface
1

Hier ist eine Funktion, die einen Pfad / Dateinamen als Zeichenfolge verwendet und die Erweiterung als Zeichenfolge zurückgibt. Es ist alles Standard-C ++ und sollte für die meisten Plattformen plattformübergreifend funktionieren.

Im Gegensatz zu mehreren anderen Antworten werden hier die ungewöhnlichen Fälle behandelt, die PathFindExtension von Windows behandelt, basierend auf der Dokumentation von PathFindExtensions.

wstring get_file_extension( wstring filename )
{
    size_t last_dot_offset = filename.rfind(L'.');
    // This assumes your directory separators are either \ or /
    size_t last_dirsep_offset = max( filename.rfind(L'\\'), filename.rfind(L'/') );

    // no dot = no extension
    if( last_dot_offset == wstring::npos )
        return L"";

    // directory separator after last dot = extension of directory, not file.
    // for example, given C:\temp.old\file_that_has_no_extension we should return "" not "old"
    if( (last_dirsep_offset != wstring::npos) && (last_dirsep_offset > last_dot_offset) )
        return L"";

    return filename.substr( last_dot_offset + 1 );
}
tfinniga
quelle
Hallo, es gibt ein Problem mit Ihrer Lösung: Es max( filename.rfind(L'\\'), filename.rfind(L'/') )werden zwei vorzeichenlose Werte verglichen, von denen einer nposdie größtmögliche vorzeichenlose Ganzzahl sein kann. Es könnte also so aussehen, als gäbe es keinen Ordner, selbst wenn er da ist!
Andrii Kovalevskyi
0

Wenn Sie zufällig Poco- Bibliotheken verwenden, können Sie Folgendes tun:

#include <Poco/Path.h>

...

std::string fileExt = Poco::Path("/home/user/myFile.abc").getExtension(); // == "abc"
Darien Pardinas
quelle
0

Wenn Sie die Erweiterung als letzten Punkt und die möglichen Zeichen danach betrachten, aber nur, wenn sie nicht das Verzeichnis-Trennzeichen enthalten, gibt die folgende Funktion den Startindex der Erweiterung zurück oder -1, wenn keine Erweiterung gefunden wurde. Wenn Sie das haben, können Sie tun, was immer Sie wollen, wie die Erweiterung entfernen, ändern, überprüfen usw.

long get_extension_index(string path, char dir_separator = '/') {
    // Look from the end for the first '.',
    // but give up if finding a dir separator char first
    for(long i = path.length() - 1; i >= 0; --i) {
        if(path[i] == '.') {
            return i;
        }
        if(path[i] == dir_separator) {
            return -1;
        }
    }
    return -1;
}
Yuval
quelle
0

Ich habe die Funktion PathFindExtension () verwendet, um festzustellen, ob es sich um eine gültige TIF-Datei handelt oder nicht.

#include <Shlwapi.h>
bool A2iAWrapperUtility::isValidImageFile(string imageFile)
{
    char * pStrExtension = ::PathFindExtension(imageFile.c_str());

    if (pStrExtension != NULL && strcmp(pStrExtension, ".tif") == 0)
    {
        return true;
    }

    return false;
}
Pabitra Dash
quelle
0

Sie können strrchr () verwenden , um das letzte Vorkommen von. (Punkt) zu finden und. (Punkt) -basierte Erweiterungsdateien abzurufen. Überprüfen Sie zum Beispiel den folgenden Code.

#include<stdio.h>

void GetFileExtension(const char* file_name) {

    int ext = '.';
    const char* extension = NULL;
    extension = strrchr(file_name, ext);

    if(extension == NULL){
        printf("Invalid extension encountered\n");
        return;
    }

    printf("File extension is %s\n", extension);
}

int main()
{
    const char* file_name = "c:\\.directoryname\\file.name.with.too.many.dots.ext";
    GetFileExtension(file_name);
    return 0;
}
HaseeB Mir
quelle