Як отримати розширення файлу з рядка в C ++


80

Враховуючи рядок "filename.conf", як перевірити частину розширення?

Мені потрібне рішення для різних платформ.

Відповіді:


34

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

Моїм улюбленим буде бібліотека файлової системи boost, яка має функцію розширення (шлях)


12
Ім'я вашого каталогу легко обробляється шляхом зворотного пошуку, хоча :).
17 від 26

30
На мою особисту думку, рішення для підвищення не слід перераховувати як відповіді на проблеми c ++. Потребувати зовнішньої бібліотеки для чогось такого простого здається дещо безглуздим.
болото

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

@OrenS Тим не менше, рішення про підвищення не слід приймати як відповідь на запитання, яке не задає питання про те, як це зробити з посиленням. Це оманливе.
Сілідроне

@MuhamedCicak ... ну, портативне рішення для othervise включає якийсь довгий шматок коду, який враховує кодування імен файлів або / та використання інших бібліотек (я підозрюю, що boost не реалізує це з нуля, а використовує інші пакети або API, де можливо). Зауважте, що навіть отримання канонічного шляху від часткового як завдання є величезною проблемою для півдюжини крайових випадків ...
Свіфт - П’ятничний пиріг

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 :: розширення тепер є частиною стандарту, перевірте, наприклад, відповідь Roi Danton нижче.
yves

42

Найкращий спосіб - це не писати код, який це робить, а викликати існуючі методи. У вікнах метод 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 отримати розширення файлу", а ваше - найпростіша відповідь, яка працює.
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, згадані іншими в потоці.


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(бібліотека є наступницею 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 .


7

Насправді, це найпростіший спосіб

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

Запам’ятайте одне: якщо '.'в назві файлу не існує, ext буде 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, оскільки це зробить ваш код набагато чистішим, ніж базікання з необробленими індексами рядків / ітераторами.

Я пропоную посилити, оскільки це високоякісна, добре перевірена, (з відкритим кодом та комерційно) безкоштовна та повністю портативна бібліотека.


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 ++. І крос-платформний.


Ви можете зменшити кількість умов. Використовуйте strlen(extension)у forстані. Тоді, якщо символ chars не відповідає, поверніть false. Зовнішній forцикл повертає true.
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

Пропозиція " Брайан Ньюман " не буде виконана для файлів file1 та file4 . і більшість інших відповідей, які базуються на зворотному пошуку, не зможуть отримати ім'я файлу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;
}

ви можете використовувати наведений вище код у вашому додатку c ++, як показано нижче:

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

Останній пункт, в деяких випадках папці присвоюється ім'я файлу як аргумент і включає крапку в назві папки, функція повертає крапку папки, так краще спочатку користувачеві перевірити, що вказане ім'я є ім'ям файлу, а не ім'ям папки.



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

3
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Це лише для Windows (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

Або ви можете використовувати це:

    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 має до цього питання? Навіщо вводити велику незалежну залежність для простої маніпуляції рядками? Якщо ви йдете цим маршрутом, чому б просто не використовувати boost?
derpface

1

Ось функція, яка приймає шлях / ім'я файлу як рядок і повертає розширення як рядок. Це все стандартний c ++, і він повинен працювати на різних платформах для більшості платформ.

На відміну від кількох інших відповідей тут, він обробляє дивні випадки, які обробляє PathFindExtention вікон, на основі документації 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найбільшим з можливих цілих беззнакових. Тож може здатися, що папки немає навіть тоді, коли вона там є!
Андрій Ковалевський

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.