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

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

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


person JeffV    schedule 09.09.2008    source источник
comment
Этот вопрос был из 2008 года, но если вы пришли сюда сегодня, см. std::filesystem:: path, который является стандартным (начиная с С++ 17) и кросс-платформенным. Как указано ниже Рой Дантон и yves.   -  person Craig Reynolds    schedule 01.03.2021


Ответы (25)


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

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

person Community    schedule 09.09.2008
comment
Однако имя вашего каталога легко обрабатывается обратным поиском :). - person 17 of 26; 09.09.2008
comment
По моему личному мнению, решения boost не должны быть указаны как ответы на проблемы c ++. Требование внешней библиотеки для чего-то такого простого кажется немного глупым. - person marsh; 03.10.2015
comment
@marsh: тем не менее, у проблемы так просто есть свои особые случаи, особенно при работе с файловыми системами - концепция, для которой почти каждая основная (и не очень) операционная система имеет свою собственную интерпретацию. Рассмотрим, например, скрытые файлы Linux (`/home/oren/.conf') или случай, упомянутый @Torlack. . @ 17 из 26, попытка упомянуть только ваше имя пользователя должна подчеркнуть проблемы, которые могут возникнуть из-за чрезмерного упрощения того, как люди используют имена в свободной форме;) - person Oren S; 09.04.2016
comment
@OrenS Тем не менее, решение для повышения не следует принимать в качестве ответа на вопрос, который не спрашивает, как это сделать с повышением. Это заблуждение. - person Silidrone; 26.08.2019
comment
@MuhamedCicak ... ну, портативное решение для othervise включает в себя некоторый длинный фрагмент кода, который учитывает кодировку имен файлов и / и использует другие библиотеки (я подозреваю, что boost не реализует его с нуля, вместо этого использует другие пакеты или API, где возможный). Обратите внимание, что даже получение канонического пути из частичного в качестве задачи является огромной проблемой с полдюжиной крайних случаев... - person Swift - Friday Pie; 07.10.2020
comment
@Swift-FridayPie Да, это гордиев узел, я полагаю, я просто выражал свое недовольство почти любым вопросом C ++, на который можно ответить с помощью Boost, даже некоторые тривиальные проблемы, которые абсолютно не требуют решения каких-либо библиотек. - person Silidrone; 08.10.2020

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

#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;
  }
}
person brian newman    schedule 09.09.2008
comment
@Что происходит, когда имя файла не имеет расширения, а предыдущая папка имеет расширение . в его названии? - person Mircea Ispas; 28.04.2013
comment
я отвечаю на вопрос; который указывает filename.conf, а не ваш гипотетический. - person brian newman; 17.05.2013
comment
По этой логике вы могли бы просто сказать return "Yes..."; вообще без проверки — подразумевается, что решение должно работать для других входных данных. В качестве еще одного встречного примера: файл с именем просто conf без расширения также вернет значение «Да...», учитывая вышеизложенное. - person Rollie; 06.07.2013
comment
Предупреждение для других: это слишком простое решение для использования в производственном коде, за исключением узких и конкретных проектов, которым не нужно обрабатывать множество реальных сценариев конечного пользователя. Разбор и обработка имен файлов нетривиальны. Я лично почти всегда использую boost::filesystem, который прост в использовании, но обеспечивает необходимую поддержку. См. boost.org/doc/libs/1_55_0/libs. /файловая система/doc/index.htm - person Dan Nissenbaum; 02.04.2014
comment
std::filesystem::path::extension теперь является частью стандарт, проверьте, например, Roi Danton ответ ниже. - person yves; 06.09.2020

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

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

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

person Torlack    schedule 09.09.2008
comment
+1 Не писать новый код часто лучший ответ! Версия C # этого была тем, что мне нужно было только что, но ваш ответ привел меня туда. msdn.microsoft.com/en-us/library/ - person Tom Resing; 04.08.2011
comment
Эта функция (в Windows 7) неправильно обрабатывает файл i.i. Да, это верно, обратите внимание на пробел. - person pcunite; 27.08.2013
comment
Он спросил о получении расширения из файла, а не полного пути. Кроме того, функция Windows API не будет хорошим ответом. Это абсолютно не ответ, а комментарий. - person Didac Perez Parera; 27.09.2013
comment
-1 за предоставление решения для конкретной платформы, когда OP запросил портативное решение. - person j b; 22.05.2014
comment
+1 От меня. Это первый вопрос, который возникает, когда вы гуглите «mfc получить расширение файла», и ваш ответ — самый простой и работающий. - person Eternal21; 19.08.2014
comment
Я предполагаю, что вы могли бы просто обернуть strchr, который находит точку, внутри вызова strchr, который сначала находит косую черту. Таким образом, папки с точками в названии не мешают. - person Taylor Hansen; 04.09.2016
comment
Хорошие моменты, но кто сказал, что это только для Windows? ОП специально упомянул кроссплатформенность. И в случае, когда упоминается полный путь, точка в имени пути, а не в имени файла, имя файла без расширения, просто также ищите разделители путей. Если перед последним разделителем пути находится точка, то имя файла не имеет расширения. Убедитесь, что путь с точкой, указанный сам по себе, является файлом, а не каталогом/папкой и т. д. Если вам нужна только эта одна функция, нет смысла привязываться к массивной библиотеке или специфике платформы. Для каждой поддерживаемой платформы создайте тестовые примеры для каждого сложного условия и обработайте его. Сверните свой собственный. - person ; 20.03.2017

Предполагая, что у вас есть доступ к 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, упомянутые другими в этой теме.

person 17 of 26    schedule 09.09.2008
comment
+1, это самое простое решение, если у вас есть файл в строке, а не путь! - person Thomas Bonini; 20.01.2010

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

#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
person peter karasev    schedule 09.10.2012
comment
+1, спасибо за публикацию фактического фрагмента кода с использованием boost. Не забудьте связать с -lboost_filesystem и вы получите рабочее решение. - person jammartin; 09.03.2021

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

person Roi Danton    schedule 15.06.2018

на самом деле 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.a.b.c.d.e.s.mp3», если оно не может найти расширение, оно вернет «».

person graphitemaster    schedule 22.12.2010
comment
разрывается с path/folder.with.dots/filename - person Ocelot; 26.06.2021

На самом деле, самый простой способ

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

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

person Qiu    schedule 30.07.2013
comment
Это не было бы идеальным решением для скрытых файлов UNIX, начинающихся с точки. - person Mark Kahn; 02.03.2014
comment
должно быть const char* ext? - person Vlad; 31.07.2015

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

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

Некоторые ответы включали метод, который я использовал в первую очередь (поиск последнего «.»), но я вспомнил, что в 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;
}

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

person serengeor    schedule 17.11.2012

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

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

person KristianR    schedule 13.12.2009

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

#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++. И кроссплатформенный.

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

Хорошие ответы, но я вижу, что у большинства из них есть некоторые проблемы: прежде всего, я думаю, что хороший ответ должен работать для полных имен файлов, у которых есть заголовки путей, а также он должен работать для 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

Предложение «brian newman» не будет работать для файлов с именем1 и с именем4. и большинство других ответов, основанных на обратном поиске, не будут работать для имени файла1. Я предлагаю включить в ваш источник следующий метод: функция, возвращающая индекс первого символа расширения или длину заданной строки, если она не найдена.

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

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

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

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

person AMCoded    schedule 08.11.2014

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

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

Я бы выбрал 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"))
{ /* ... */ }
person manlio    schedule 02.05.2015

_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Это только для Windows (платформенный SDK)

person Aardvark    schedule 09.09.2008

Это решение, которое я придумал. Затем я заметил, что это похоже на то, что опубликовал @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;
}
person Mike Finch    schedule 19.08.2015

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

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;

}

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

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

Примечание.

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

person Jahid    schedule 19.08.2015

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

char* lastSlash;
lastSlash = strstr(filename, ".");
person Maadiah    schedule 11.05.2012

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

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

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

person Quest    schedule 07.08.2013

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

person Mark Kahn    schedule 02.03.2014
comment
Какое отношение имеет Qt к этому вопросу? Зачем вводить большую стороннюю зависимость для простых манипуляций со строками? Если идти по этому пути, почему бы просто не использовать boost? - person derpface; 15.07.2014

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

В отличие от нескольких других ответов здесь, он обрабатывает нечетные случаи, которые обрабатывает Windows PathFindExtension, на основе документации 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 );
}
person tfinniga    schedule 28.04.2014
comment
Привет, есть проблема с вашим решением: max( filename.rfind(L'\\'), filename.rfind(L'/') ) будет сравнивать два значения без знака, одно из них может быть npos, которое является максимально возможным целым числом без знака. Таким образом, может показаться, что папки нет, даже если она есть! - person Andrii Kovalevskyi; 30.04.2015

Вы можете использовать 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;
}
person HaseeB Mir    schedule 26.01.2020

Если вы рассматриваете расширение как последнюю точку и возможные символы после нее, но только если они не содержат символа разделителя каталогов, следующая функция возвращает начальный индекс расширения или -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;
}
person Yuval    schedule 17.12.2015

Я использовал функцию 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;
}
person Pabitra Dash    schedule 20.01.2016

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

#include <Poco/Path.h>

...

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