Как получить расширение файла из строки в C ++

80

Учитывая строку "filename.conf", как мне проверить часть расширения?

Мне нужно кроссплатформенное решение.

JeffV
источник

Ответы:

34

Вы должны позаботиться о том, чтобы имена файлов состояли более чем из одной точки. пример: c:\.directoryname\file.name.with.too.many.dots.extне будет правильно обработан strchrилиfind.

Мне больше всего нравится библиотека файловой системы boost с функцией расширения (пути).

Томас Бонини
источник
12
Имя вашего каталога легко обрабатывается обратным поиском :).
17 из 26,
30
По моему личному мнению, решения boost не следует указывать как ответы на проблемы C ++. Требовать внешней библиотеки для чего-то такого простого кажется немного глупым.
Марш
4
@marsh: тем не менее, у такой простой проблемы есть свои особые случаи, особенно когда речь идет о файловых системах - концепции, для которой почти каждая основная (и не такая серьезная) операционная система имеет свою собственную интерпретацию. Рассмотрим, например, скрытые файлы linux (`/home/oren/.conf ') или случай, упомянутый @Torlack . @ 17 из 26, попытка упомянуть только свое имя пользователя должна высветить проблемы, которые могут возникнуть из-за чрезмерного упрощения того, как люди используют имена в свободной форме;)
Oren S
@OrenS Тем не менее, решение boost никогда не следует принимать в качестве ответа на вопрос, который не спрашивает, как это сделать с помощью boost. Это заблуждение.
Silidrone
@MuhamedCicak ... ну, переносимое решение для othervise включает в себя некоторый длинный фрагмент кода, который учитывает кодировку учетных записей имен файлов или / и использует другие библиотеки (я подозреваю, что boost не реализует его с нуля, вместо этого использует другие пакеты или API, где возможно). Обратите внимание, что даже получение канонического пути из частичного в качестве задачи - огромная проблема с полдюжиной крайних случаев ...
Swift - Friday Pie
156

Это слишком простое решение?

#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
@ Что происходит, если имя файла не имеет расширения, а у предыдущей папки есть. в его имени?
Мирча Испас
4
Отвечаю на вопрос; который указывает "filename.conf", а не ваш гипотетический.
Брайан Ньюман
5
По этой логике вы могли бы просто сказать return "Yes...";без проверки - это подразумевает, что решение должно работать для других входов. В качестве другого примера счетчика файл с именем просто «conf» без расширения также вернет «Да ...» с учетом вышеизложенного.
Rollie
4
Предупреждение для других: это слишком простое решение для использования в производственном коде, за исключением узких и специфических проектов, которым не нужно обрабатывать множество реальных сценариев конечного пользователя. Разбор и обработка имени файла нетривиальны. Лично я почти всегда использую boost::filesystem, что тривиально в использовании, но обеспечивает необходимую поддержку. См. Boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm
Дэн Ниссенбаум
1
std :: filesystem :: path :: extension теперь является частью стандарта, проверьте, например, ответ Роя Дантона ниже.
Ив
42

Лучший способ - не писать код, который это делает, а вызывать существующие методы. В Windows метод PathFindExtension, наверное, самый простой.

Так почему бы вам не написать свой собственный?

Что ж, возьмем пример strrchr. Что произойдет, если вы используете этот метод со следующей строкой «c: \ program files \ AppleGate.Net \ readme»? Является ли ".Net \ readme" расширением? Легко написать что-то, что работает для нескольких примеров, но может быть намного сложнее написать что-то, что работает для всех случаев.

Торлак
источник
3
+1 Часто лучший ответ - не писать новый код! Это была версия C #, которую я искал только что, но ваш ответ привел меня туда. msdn.microsoft.com/en-us/library/…
Том Ресинг
Эта функция (в Windows 7) неправильно обрабатывает "file.i i". Да, это действительно так, обратите внимание на пробел.
pcunite
Он спросил об извлечении расширения из файла, а не полного пути. Кроме того, функция Windows API не будет хорошим ответом. Это абсолютно не ответ, а комментарий.
Didac Perez Parera
4
-1 для предоставления решения для конкретной платформы, когда OP запросил переносимое решение.
jb
+1 От меня. Этот вопрос - первый, который возникает, когда вы гуглите "mfc get file extension", и ваш самый простой ответ, который работает.
Eternal21,
32

Предполагая, что у вас есть доступ к 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
}

Изменить: это кроссплатформенное решение, поскольку вы не упомянули платформу. Если вы специально работаете с Windows, вы захотите использовать специальные функции Windows, упомянутые другими в потоке.

17 из 26
источник
6
+1, это простейшее решение, если у вас есть файл в строке, а не в пути!
Thomas Bonini
25

Кто-то еще упомянул повышение, но я просто хотел добавить для этого код:

#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

на самом деле STL может сделать это без большого количества кода, я советую вам немного узнать о STL, потому что он позволяет вам делать некоторые необычные вещи, в любом случае это то, что я использую.

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

это решение всегда будет возвращать расширение даже для таких строк, как "this.abcdesmp3", если оно не может найти расширение, которое вернет "".

графитмастер
источник
15

С C ++ 17 и его std::filesystem::path::extension(библиотека является преемником повышающего :: файловой системы) , вы бы сделать ваше утверждение более выразительным , чем при использовании , например 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"
    }
}

См. Также std :: filesystem :: path :: stem , std :: filesystem :: path :: filename .

Рой Дантон
источник
7

Собственно, самый простой способ -

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

Следует помнить одну вещь: если '.'в имени файла не существует, будет расширение NULL.

Цю
источник
4
Это не было бы идеальным решением для скрытых файлов UNIX, которые начинаются с точки
Марк Кан
должно быть const char * ext?
Влад
4

Я сегодня сам наткнулся на этот вопрос, хотя у меня уже был рабочий код, я понял, что в некоторых случаях он не будет работать.

Хотя некоторые люди уже предлагали использовать некоторые внешние библиотеки, я предпочитаю писать свой собственный код в учебных целях.

Некоторые ответы включали метод, который я использовал в первую очередь (поиск последнего "."), Но я вспомнил, что в Linux скрытые файлы / папки начинаются с ".". Таким образом, если файл скрыт и не имеет расширения, для расширения будет использовано все имя файла. Чтобы этого избежать, я написал этот фрагмент кода:

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

Я не тестировал это полностью, но думаю, что это должно работать.

серенджер
источник
3

Использование std :: string find / rfind решает ЭТУ проблему, но если вы много работаете с путями, вам следует взглянуть на boost :: filesystem :: path, поскольку это сделает ваш код намного чище, чем возиться с необработанными строковыми индексами / итераторами.

Я предлагаю boost, поскольку это высококачественная, хорошо протестированная (с открытым исходным кодом и коммерчески) бесплатная и полностью переносимая библиотека.

KristianR
источник
3

Для строк типа массива char вы можете использовать это:

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

Может обрабатывать пути к файлам в дополнение к именам файлов. Работает как с C, так и с C ++. И кроссплатформенный.

delaccount992
источник
Вы можете уменьшить количество условий. Использование strlen(extension)в forсостоянии. Затем, если символы не совпадают, верните false. Внешний forцикл возвращает истину.
LRDPRDX
3

Хорошие ответы, но я вижу, что у большинства из них есть некоторые проблемы: Прежде всего, я думаю, что хороший ответ должен работать для полных имен файлов, у которых есть заголовки пути, также он должен работать для Linux или Windows или, как уже упоминалось, должен быть кросс-платформенным. Для большинства ответов; имена файлов без расширения, но путь с именем папки, включая точку, функция не сможет вернуть правильное расширение: примеры некоторых тестовых случаев могут быть следующими:

    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

Предложение " Брайан Ньюман " не будет выполнено для filename1 и filename4. и большинство других ответов, основанных на обратном поиске, не удастся для filename1. Я предлагаю включить в ваш источник следующий метод: функция, возвращающая индекс первого символа расширения или длину данной строки, если она не найдена.

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

вы можете использовать приведенный выше код в своем приложении на C ++, как показано ниже:

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

Последняя точка в некоторых случаях, когда папка дается имени файла в качестве аргумента и включает точку в имени папки, функция возвращает конечную точку папки, поэтому лучше сначала пользователю проверить, является ли данное имя именем файла, а не именем папки.

AMCoded
источник
3

Версия NET / CLI с использованием System :: String

   System::String^ GetFileExtension(System::String^ FileName)
   {
       int Ext=FileName->LastIndexOf('.');
       if( Ext != -1 )
           return FileName->Substring(Ext+1);
       return "";
   }
Леопольдо Санчик
источник
Это не Visual C ++, это .NET / CLI .
Виктор
1
@Victor Я редактировал ответ. Спасибо за разъяснение.
Леопольдо Санчик
3

Я бы пошел boost::filesystem::extension( std::filesystem::path::extensionс C ++ 17), но если вы не можете использовать Boost и вам просто нужно проверить расширение, простое решение:

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
источник
3
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Это только Windows (Platform SDK)

Трубкозуб
источник
2

Это решение, которое я придумал. Затем я заметил, что это похоже на то, что опубликовал @serengeor.

Он работает с std::stringи find_last_of, но основная идея также будет работать, если изменить использование charмассивов и strrchr. Он обрабатывает скрытые файлы и дополнительные точки, представляющие текущий каталог. Он не зависит от платформы.

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

Модульный тест:

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

Я использую эти две функции, чтобы получить расширение и имя файла без расширения :

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

И эти regexподходы для определенных дополнительных требований:

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;

}

Дополнительные требования, которым отвечает метод регулярного выражения:

  1. Если имя файла похоже .configили что-то в этом роде, расширение будет пустой строкой, а имя файла без расширения будет .config.
  2. Если имя файла не имеет расширения, расширение будет пустой строкой, имя файла без расширения будет именем файла без изменений.

РЕДАКТИРОВАТЬ:

Дополнительные требования также могут быть выполнены за счет следующего:

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

Заметка:

Передайте только имена файлов (не путь) в вышеуказанных функциях.

Джахид
источник
1

Попробуйте использовать strstr

char* lastSlash;
lastSlash = strstr(filename, ".");
Маадия
источник
1

Или вы можете использовать это:

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

Этот код кроссплатформенный

Квест
источник
1

Если вы используете библиотеку Qt, вы можете дать попробовать на QFileInfo «s суффикса ()

Марк Кан
источник
2
Какое отношение имеет Qt к этому вопросу? Зачем вводить большую стороннюю зависимость для простой обработки строк? Если пойти по этому пути, почему бы просто не использовать ускорение?
derpface
1

Вот функция, которая принимает путь / имя файла в виде строки и возвращает расширение в виде строки. Все это стандартный C ++, и он должен работать на разных платформах.

В отличие от нескольких других ответов здесь, он обрабатывает нечетные случаи, которые обрабатывает PathFindExtension Windows, на основе документации 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
источник
Привет, есть проблема с вашим решением: max( filename.rfind(L'\\'), filename.rfind(L'/') )сравнит два значения без знака, одно из них может быть nposмаксимально возможным целым числом без знака. Так что может показаться, что папки нет, даже если она есть!
Андрей Ковалевский
0

Если вы используете библиотеки Poco, вы можете:

#include <Poco/Path.h>

...

std::string fileExt = Poco::Path("/home/user/myFile.abc").getExtension(); // == "abc"
Дариен Пардинас
источник
0

Если вы рассматриваете расширение как последнюю точку и возможные символы после нее, но только если они не содержат символа разделителя каталогов, следующая функция возвращает начальный индекс расширения или -1, если расширение не найдено. Когда у вас есть это, вы можете делать все, что захотите, например, удалить расширение, изменить его, проверить и т. Д.

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

Я использовал функцию PathFindExtension (), чтобы узнать, действительный ли это файл tif или нет.

#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

Вы можете использовать strrchr (), чтобы найти последнее вхождение файлов расширений. (Точка) и получить. (Точка). Например, проверьте приведенный ниже код.

#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;
}
ХасиБ Мир
источник