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

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

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

Этот вопрос был задан в 2008 году, но если вы пришли сюда сегодня, см. std :: filesystem :: путь, который является стандартным (начиная с C++ 17) и кроссплатформенным. Как указано ниже Рой Дантон и Ив.

Craig Reynolds 01.03.2021 20:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
82
1
179 157
25
Перейти к ответу Данный вопрос помечен как решенный

Ответы 25

_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

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

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

+1, это простейшее решение, если у вас есть файл в строке, а не в пути!

Thomas Bonini 20.01.2010 23:55

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

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

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

+1 Часто лучший ответ - не писать новый код! Это была версия C#, которую я искал только что, но ваш ответ привел меня туда. msdn.microsoft.com/en-us/library/…

Tom Resing 04.08.2011 18:53

Эта функция (в Windows 7) некорректно обрабатывает "file.i i". Да, это действительно так, обратите внимание на пробел.

pcunite 27.08.2013 22:21

Он спросил об извлечении расширения из файла, а не полного пути. Кроме того, функция Windows API не будет хорошим ответом. Это абсолютно не ответ, а комментарий.

Didac Perez Parera 27.09.2013 13:55

-1 для предоставления решения для конкретной платформы, когда OP запросил переносимое решение.

j b 22.05.2014 19:35

+1 От меня. Этот вопрос - первый, который возникает, когда вы гуглите "mfc get file extension", и ваш самый простой ответ, который работает.

Eternal21 19.08.2014 17:10

Я предполагаю, что вы могли бы просто обернуть strchr, который находит точку внутри вызова strchr, который первым находит косую черту. Таким образом, папки с точками в названии не будут мешать.

Taylor Hansen 04.09.2016 19:14

Хорошие моменты, но кто сказал, что это только в Windows? ОП специально упомянул кроссплатформенность. И для упомянутого случая полного пути, точка в имени пути не в имени файла, имя файла без расширения, просто также ищите разделители пути. Если точка находится перед последним разделителем пути, имя файла не имеет расширения. Убедитесь, что путь с точкой, указанный сам по себе, является файлом, а не каталогом / папкой и т. д. Если нужна только эта одна функция, нет смысла связываться с массивной библиотекой или особенностями платформы. Для каждой поддерживаемой платформы создайте тестовые примеры для каждого «сложного» условия и обработайте его. Сверните свой собственный.

user2895783 20.03.2017 17:34

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

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

@ Что происходит, если имя файла не имеет расширения, а у предыдущей папки есть. в его имени?

Mircea Ispas 28.04.2013 23:59

Отвечаю на вопрос; который указывает "filename.conf", а не ваш гипотетический.

brian newman 17.05.2013 06:40

По этой логике вы можете просто сказать return "Yes..."; без проверки - это подразумевает, что решение должно работать для других входов. В качестве другого примера счетчика файл с именем просто «conf» без расширения также вернет «Да ...» с учетом вышеизложенного.

Rollie 06.07.2013 00:43

Предупреждение для других: это слишком простое решение для использования в производственном коде, за исключением узких и конкретных проектов, которым не нужно обрабатывать множество реальных сценариев конечного пользователя. Разбор и обработка имени файла нетривиальны. Лично я почти всегда использую boost::filesystem, который прост в использовании, но обеспечивает необходимую поддержку. См. boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm

Dan Nissenbaum 02.04.2014 13:33
std :: filesystem :: path :: extension is now part of the standard, check for eg Рой Дантон answer below.
yves 06.09.2020 12:49
Ответ принят как подходящий

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

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

Имя вашего каталога легко обрабатывается обратным поиском :).

17 of 26 09.09.2008 18:33

По моему личному мнению, решения boost не следует указывать как ответы на проблемы C++. Требовать внешней библиотеки для чего-то такого простого кажется немного глупым.

marsh 03.10.2015 03:37

@marsh: тем не менее, проблема так просто имеет свои особые случаи, особенно когда речь идет о файловых системах - концепция, для которой почти каждая основная (и не очень) операционная система имеет свою интерпретацию. Рассмотрим, например, скрытые файлы linux (`/home/oren/.conf ') или случай упомянутый @Torlack. @ 17 из 26, попытка упомянуть только свое имя пользователя должна выявить проблемы, которые могут возникнуть из-за чрезмерного упрощения того, как люди используют именование в свободной форме;)

Oren S 09.04.2016 17:10

@OrenS Тем не менее, решение boost никогда не следует принимать как ответ на вопрос, который не спрашивает, как это сделать с помощью boost. Это заблуждение.

Silidrone 26.08.2019 17:59

@MuhamedCicak ... ну, портативное решение для othervise включает в себя некоторый длинный фрагмент кода, который учитывает кодировку учетных записей имен файлов или / и использует другие библиотеки (я подозреваю, что boost не реализует его с нуля, вместо этого использует другие пакеты или API, где возможный). Обратите внимание, что даже получение канонического пути из частичного в качестве задачи - огромная проблема с полдюжиной крайних случаев ...

Swift - Friday Pie 07.10.2020 20:22

@ Swift-FridayPie Да, это гордиев узел. Полагаю, я просто выражал свое разочарование по поводу почти любого вопроса C++, на который отвечал с помощью Boost, даже некоторых тривиальных проблем, которые абсолютно не требуют решения каких-либо библиотек.

Silidrone 08.10.2020 07:36

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

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

на самом деле 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», если оно не может найти расширение, которое вернет «».

Для строк типа массива 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++. И кроссплатформенность.

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

LRDPRDX 16.02.2018 22:44

Версия 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.

Victor 15.02.2016 10:50

@Victor Я редактировал ответ. Спасибо за разъяснение.

Leopoldo Sanczyk 16.02.2016 16:01

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

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

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

#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

+1, спасибо за размещение фактического фрагмента кода с использованием boost. Не забудьте связать с -lboost_filesystem, и вы получите рабочее решение.

jammartin 09.03.2021 15:29

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

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

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

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

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

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

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

Это не было бы идеальным решением для скрытых файлов UNIX, начинающихся с точки.

Mark Kahn 02.03.2014 15:01

должно быть const char * ext?

Vlad 31.07.2015 01:53

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

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

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

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

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

derpface 15.07.2014 19:17

Вот функция, которая принимает путь / имя файла в виде строки и возвращает расширение в виде строки. Все это стандартный 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 );
}

Привет, есть проблема с вашим решением: max( filename.rfind(L'\'), filename.rfind(L'/') ) сравнит два значения без знака, одно из них может быть npos, которое является наибольшим возможным целым числом без знака. Так что может показаться, что папки нет, даже если она есть!

Andrii Kovalevskyi 30.04.2015 14:41

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

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

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

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

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

Примечание:

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

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

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

#include <Poco/Path.h>

...

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

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

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

С 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 :: файловая система :: путь :: стержень, std :: filesystem :: path :: filename.

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

Другие вопросы по теме