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


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)


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

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.


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.


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

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 „”.


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 .


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.


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ć.


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.


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.


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.


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

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"))
{ /* ... */ }

3
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

To jest tylko Windows (Platform SDK)


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

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.



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


1

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


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

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"

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

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

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;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.