Jak uzyskać rozszerzenie pliku z ciągu znaków w C ++

80

Biorąc pod uwagę ciąg "filename.conf", jak zweryfikować część rozszerzenia?

Potrzebuję rozwiązania wieloplatformowego.

JeffV
źródło

Odpowiedzi:

34

Musisz upewnić się, że dbasz o nazwy plików z więcej niż jedną kropką. przykład: c:\.directoryname\file.name.with.too.many.dots.extnie zostanie poprawnie obsłużony przez strchrlubfind.

Moją ulubioną byłaby biblioteka systemu plików boost, która ma funkcję rozszerzenia (ścieżki)

Thomas Bonini
źródło
12
Nazwa katalogu jest łatwa do odczytania przez odwrotne wyszukiwanie :).
17 z 26
30
Moim zdaniem rozwiązania boost nie powinny być wymieniane jako odpowiedzi na problemy z c ++. Wymaganie zewnętrznej biblioteki do czegoś tak prostego wydaje się trochę głupie.
bagno
4
@marsh: jednak tak prosty problem ma swoje szczególne przypadki, zwłaszcza w przypadku systemów plików - koncepcja, dla której prawie każdy większy (i mniej poważny) system operacyjny ma swoją własną interpretację. Rozważmy np. Ukryte pliki Linuksa (`/home/oren/.conf ') lub przypadek wspomniany przez @Torlack . @ 17 z 26, próba podania samej nazwy użytkownika powinna zwrócić uwagę na problemy, które mogą wyniknąć z nadmiernego uproszczenia sposobu, w jaki ludzie używają nazw swobodnych;)
Oren S
@OrenS Niemniej jednak rozwiązanie boost nie powinno być nigdy akceptowane jako odpowiedź na pytanie, które nie dotyczy tego, jak to zrobić z boostem. To jest mylące.
Silidrone
@MuhamedCicak ... cóż, przenośne rozwiązanie dla othervise obejmuje długi fragment kodu, który bierze pod uwagę kodowanie nazw plików lub / i korzysta z innych bibliotek (podejrzewam, że boost nie implementuje go od zera, zamiast tego używa innych pakietów lub API, w których możliwy). Zwróć uwagę, że nawet uzyskanie ścieżki kanonicznej z częściowej jako zadania jest ogromnym problemem z pół tuzinem skrajnych przypadków ...
Swift - Friday Pie
156

Czy to zbyt proste rozwiązanie?

#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
źródło
12
@Co się dzieje, gdy nazwa pliku nie ma rozszerzenia, a poprzedni folder ma. w jego imieniu?
Mircea Ispas
4
Odpowiadam na pytanie; który określa „nazwa_pliku.conf”, a nie hipotetyczny.
brian newman
5
Kierując się tą logiką, możesz po prostu powiedzieć return "Yes...";bez sprawdzenia - zakłada się, że rozwiązanie powinno działać dla innych danych wejściowych. Jako inny przeciwny przykład, plik o nazwie po prostu „conf” bez rozszerzenia również zwróciłby „Tak ...”, biorąc pod uwagę powyższe.
Rollie,
4
Ostrzeżenie dla innych: jest to zbyt proste rozwiązanie, aby można je było wykorzystać w kodzie produkcyjnym, z wyjątkiem wąskich i specyficznych projektów, które nie muszą obsługiwać różnych rzeczywistych scenariuszy użytkowników końcowych. Analiza i obsługa nazw plików są nietrywialne. Osobiście prawie zawsze używam boost::filesystem, co jest trywialne w użyciu, ale zapewnia niezbędne wsparcie. Zobacz boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm
Dan Nissenbaum
1
Rozszerzenie std :: filesystem :: path :: jest teraz częścią standardu, sprawdź np. odpowiedź Roi Danton poniżej.
yves
42

Najlepszym sposobem jest nie pisanie kodu, który to robi, ale wywołanie istniejących metod. W systemie Windows metoda PathFindExtension jest prawdopodobnie najprostsza.

Więc dlaczego nie miałbyś napisać własnego?

Cóż, weźmy przykład strrchr, co się stanie, gdy użyjesz tej metody w następującym ciągu „c: \ program files \ AppleGate.Net \ readme”? Czy „.Net \ readme” jest rozszerzeniem? Łatwo jest napisać coś, co działa w kilku przykładowych przypadkach, ale może być znacznie trudniej napisać coś, co działa we wszystkich przypadkach.

Torlack
źródło
3
+1 Brak pisania nowego kodu jest często najlepszą odpowiedzią! Wersja C # tego była tym, czego właśnie szukałem, ale Twoja odpowiedź mnie tam doprowadziła. msdn.microsoft.com/en-us/library/…
Tom Resing
Ta funkcja (w systemie Windows 7) nie będzie poprawnie obsługiwać pliku „file.i i”. Tak, to ważne, zwróć uwagę na przestrzeń.
pcunite
Poprosił o pobranie rozszerzenia z pliku, a nie o pełną ścieżkę. Ponadto funkcja Windows API nie byłaby dobrą odpowiedzią. To absolutnie nie jest odpowiedź, ale komentarz.
Didac Perez Parera
4
-1 za zapewnienie rozwiązania specyficznego dla platformy, gdy OP zażądał rozwiązania przenośnego.
jb
+1 ode mnie. To pytanie jest pierwszym, które pojawia się, gdy wyszukujesz w Google „rozszerzenie pliku mfc get”, a Twoja najprostsza odpowiedź działa.
Eternal
32

Zakładając, że masz dostęp do 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
}

Edycja: jest to rozwiązanie wieloplatformowe, ponieważ nie wspomniałeś o platformie. Jeśli korzystasz z systemu Windows, będziesz chciał wykorzystać specyficzne funkcje systemu Windows wspomniane przez innych w wątku.

17 z 26
źródło
6
+1, to najprostsze rozwiązanie w przypadku, gdy masz plik w ciągu znaków, a nie ścieżkę!
Thomas Bonini,
25

Ktoś inny wspomniał o wzmocnieniu, ale chciałem tylko dodać rzeczywisty kod, aby to zrobić:

#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
źródło
20

w rzeczywistości STL może to zrobić bez dużej ilości kodu, radzę dowiedzieć się trochę o STL, ponieważ pozwala ci zrobić kilka wymyślnych rzeczy, w każdym razie tego używam.

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

to rozwiązanie zawsze zwróci rozszerzenie, nawet w przypadku łańcuchów typu „this.abcdesmp3”, jeśli nie może znaleźć rozszerzenia, zwróci „”.

grafitemaster
źródło
15

Z C ++ 17 i jego std::filesystem::path::extension(biblioteka jest następcą boost :: filesystem) uczyniłbyś swoją instrukcję bardziej ekspresyjną niż użycie np 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"
    }
}

Zobacz także std :: filesystem :: path :: stem , std :: filesystem :: path :: filename .

Roi Danton
źródło
7

Właściwie najłatwiej jest

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

Jedna rzecz do zapamiętania: jeśli '.'nie istnieje w nazwie pliku, ext będzie NULL.

Qiu
źródło
4
To nie byłoby idealne rozwiązanie dla ukrytych plików UNIX, które zaczynają się od kropki
Mark Kahn
czy powinno to być const char * ext?
Vlad
4

Sam natknąłem się dzisiaj na to pytanie, chociaż miałem już działający kod, zorientowałem się, że w niektórych przypadkach nie zadziała.

Podczas gdy niektórzy już sugerowali użycie zewnętrznych bibliotek, ja wolę pisać własny kod do celów edukacyjnych.

Niektóre odpowiedzi zawierały metodę, której użyłem w pierwszej kolejności (szukanie ostatniego „.”), Ale pamiętałem, że w Linuksie ukryte pliki / foldery zaczynają się od „.”. Więc jeśli plik jest ukryty i nie ma rozszerzenia, cała nazwa pliku zostanie wzięta za rozszerzenie. Aby tego uniknąć, napisałem ten fragment kodu:

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

Nie testowałem tego w pełni, ale myślę, że powinno działać.

serengeor
źródło
3

Użycie funkcji find / rfind std :: string rozwiązuje TEN problem, ale jeśli dużo pracujesz ze ścieżkami, powinieneś spojrzeć na boost :: filesystem :: path, ponieważ sprawi, że twój kod będzie znacznie czystszy niż majstrowanie przy indeksach / iteratorach ciągów.

Sugeruję ulepszenie, ponieważ jest to wysokiej jakości, dobrze przetestowana (open source i komercyjnie) bezpłatna iw pełni przenośna biblioteka.

KristianR
źródło
3

W przypadku łańcuchów znaków typu tablica możesz użyć tego:

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

Obsługuje ścieżki plików oprócz nazw plików. Działa z C i C ++. I wieloplatformowy.

delaccount992
źródło
Możesz zmniejszyć liczbę warunków. Używać strlen(extension)w forstanie. Następnie, jeśli znaki nie pasują, zwróć false. forPętla zewnętrzna zwraca prawdę.
LRDPRDX
3

Dobre odpowiedzi, ale widzę, że większość z nich ma pewne problemy: Przede wszystkim uważam, że dobra odpowiedź powinna działać w przypadku pełnych nazw plików, które mają swoje nagłówki ścieżek, a także powinna działać dla systemu Linux lub Windows lub, jak wspomniano, powinna być wieloplatformowa. W przypadku większości odpowiedzi; nazwy plików bez rozszerzenia, ale ścieżka z nazwą folderu zawierającą kropkę, funkcja nie zwróci prawidłowego rozszerzenia: przykłady niektórych przypadków testowych mogą wyglądać następująco:

    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

Sugestia „ brian newman ” nie powiedzie się dla nazwy pliku1 i nazwy pliku4. a większość innych odpowiedzi opartych na wyszukiwaniu wstecznym nie powiedzie się dla nazwy_pliku1. Proponuję uwzględnić w źródle następującą metodę: która jest funkcją zwracającą indeks pierwszego znaku rozszerzenia lub długość podanego ciągu, jeśli nie zostanie znaleziony.

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

możesz użyć powyższego kodu w swojej aplikacji c ++, jak poniżej:

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

W ostatnim punkcie w niektórych przypadkach folder a jest podawany jako argument jako nazwa pliku i zawiera kropkę w nazwie folderu, funkcja zwróci kropkę folderu na końcu, aby użytkownik najpierw sprawdził, czy podana nazwa jest nazwą pliku, a nie nazwą folderu.

AMCoded
źródło
3

Wersja NET / CLI używająca System :: String

   System::String^ GetFileExtension(System::String^ FileName)
   {
       int Ext=FileName->LastIndexOf('.');
       if( Ext != -1 )
           return FileName->Substring(Ext+1);
       return "";
   }
Leopoldo Sanczyk
źródło
To nie jest Visual C ++, to .NET / CLI .
Victor,
1
@Victor Poprawiłem odpowiedź. Dzięki za wyjaśnienie.
Leopoldo Sanczyk
3

Poszedłbym z boost::filesystem::extension( std::filesystem::path::extensionz C ++ 17), ale jeśli nie możesz użyć Boost i musisz tylko zweryfikować rozszerzenie, prostym rozwiązaniem jest:

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
źródło
3
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

To jest tylko Windows (Platform SDK)

Aardvark
źródło
2

To jest rozwiązanie, które wymyśliłem. Potem zauważyłem, że jest podobny do tego, co opublikował @serengeor.

Działa z std::stringi find_last_of, ale podstawowa idea zadziała również, jeśli zostanie zmodyfikowana do używania chartablic i strrchr. Obsługuje ukryte pliki i dodatkowe kropki reprezentujące bieżący katalog. Jest niezależny od platformy.

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

Test jednostkowy:

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
źródło
2

Używam tych dwóch funkcji, aby uzyskać rozszerzenie i nazwę pliku bez rozszerzenia :

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

I te regexpodejścia dla pewnych dodatkowych wymagań:

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;

}

Dodatkowe wymagania, które spełnia metoda regex:

  1. Jeśli nazwa pliku jest podobna .configlub podobna do tego, rozszerzenie będzie pustym ciągiem, a nazwa pliku bez rozszerzenia będzie.config .
  2. Jeśli nazwa pliku nie ma żadnego rozszerzenia, rozszerzenie będzie pustym ciągiem, a nazwa pliku bez rozszerzenia będzie nazwą pliku niezmienioną.

EDYTOWAĆ:

Dodatkowe wymagania mogą być również spełnione przez:

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

Uwaga:

Przekaż tylko nazwy plików (nie ścieżki) w powyższych funkcjach.

Jahid
źródło
1

Spróbuj użyć strstr

char* lastSlash;
lastSlash = strstr(filename, ".");
Maadiah
źródło
1

Lub możesz użyć tego:

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

Ten kod jest wieloplatformowy

Zadanie
źródło
1

Jeśli korzystasz z biblioteki Qt, można dać spróbować QFileInfo jest przyrostkiem ()

Mark Kahn
źródło
2
Co Qt ma wspólnego z tym pytaniem? Po co wprowadzać dużą zależność od innej firmy w celu prostej manipulacji na ciągach znaków? Jeśli wybierasz się tą trasą, dlaczego nie użyć po prostu doładowania?
derpface
1

Oto funkcja, która pobiera ścieżkę / nazwę pliku jako ciąg i zwraca rozszerzenie jako ciąg. To wszystko jest w standardzie C ++ i powinno działać na wielu platformach dla większości platform.

W przeciwieństwie do kilku innych odpowiedzi tutaj, obsługuje dziwne przypadki, które obsługuje PathFindExtension systemu Windows, na podstawie dokumentacji 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
źródło
Cześć, jest problem z twoim rozwiązaniem: max( filename.rfind(L'\\'), filename.rfind(L'/') )porówna dwie wartości bez znaku, jedną z nich może być nposnajwiększa możliwa liczba całkowita bez znaku. Więc może wyglądać, że nie ma folderu, nawet jeśli tam jest!
Andrii Kovalevskyi
0

Jeśli korzystasz z bibliotek Poco , możesz:

#include <Poco/Path.h>

...

std::string fileExt = Poco::Path("/home/user/myFile.abc").getExtension(); // == "abc"
Darien Pardinas
źródło
0

Jeśli uznasz rozszerzenie za ostatnią kropkę i możliwe znaki po niej, ale tylko wtedy, gdy nie zawierają one znaku separatora katalogu, poniższa funkcja zwraca indeks początkowy rozszerzenia lub -1, jeśli nie znaleziono rozszerzenia. Kiedy już to masz, możesz robić, co chcesz, na przykład zdjąć rozszerzenie, zmienić, sprawdzić itp.

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
źródło
0

Użyłem funkcji PathFindExtension (), aby dowiedzieć się, czy jest to prawidłowy plik tif, czy nie.

#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
źródło
0

Możesz użyć strrchr (), aby znaleźć ostatnie wystąpienie plików rozszerzeń. (Kropka) i pobrać. (Kropka). Sprawdź na przykład poniższy kod.

#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
źródło