24 ответов:
вы должны убедиться, что вы заботитесь о имена файлов с более чем одной точкой. пример:
c:\.directoryname\file.name.with.too.many.dots.extне будет правильно обрабатыватьсяstrchrилиfind.мой любимый будет boost filesystem library которые имеют функцию расширения (путь)
это слишком простое решение?
#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; } }
лучший способ-не писать код, который это делает, но вызывать существующие методы. В windows, the PathFindExtension метод, вероятно, самый простой.
Так почему бы вам не написать свой собственный?
ну, возьмем пример strrchr, что происходит, когда вы используете этот метод на следующей строке "c:\program files\AppleGate. Net\readme"? является ли расширение ".Net\readme"? Легко написать что-то, что работает для нескольких примеров, но может быть много труднее написать что-то, что работает для всех случаев.
предполагая, что у вас есть доступ к 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 }Edit: это кросс-платформенное решение, так как вы не упомянули платформу. Если вы специально работаете в Windows, вы захотите использовать конкретные функции Windows, упомянутые другими участниками потока.
кто-то еще упомянул boost, но я просто хотел добавить фактический код для этого:
#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
на самом деле 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* ext; ext = strrchr(filename,'.')одна вещь, чтобы помнить: если
'.'не существует в filename, ext будетNULL.
Я сам наткнулся на этот вопрос сегодня, хотя у меня уже был рабочий код, я понял, что в некоторых случаях он не будет работать.
хотя некоторые люди уже предложили использовать некоторые внешние библиотеки, я предпочитаю писать свой собственный код для учебных целей.
некоторые ответы включали метод, который я использовал в первую очередь (ищу последний "."), но я вспомнил, что в 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; }Я не проверял это полностью, но я думаю, что это должно работать.
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_wЯ думаю, что это только Windows (Platform SDK)?
использование std:: string's find / rfind решает эту проблему, но если вы много работаете с путями, то вам следует посмотреть на boost::filesystem::path, поскольку это сделает ваш код намного чище, чем возиться с необработанными строковыми индексами/итераторами.
Я предлагаю повысить, так как это высококачественная, хорошо протестированная (с открытым исходным кодом и коммерчески) бесплатная и полностью портативная библиотека.
для строк типа массива 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++. И кросс-платформенный.
хорошие ответы, но я вижу, что большинство из них имеет некоторые проблемы: Прежде всего, я думаю, что хороший ответ должен работать для полных имен файлов, которые имеют свои заголовки пути, также он должен работать для 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)); }последний пункт в некоторых случаях папка a присваивается имени файла в качестве аргумента и включает точку в имени папки функция будет возвращать точку в конце папки, поэтому лучше сначала проверить, что данное имя является именем файла, а не именем папки.
версия NET/CLI с использованием System:: String
System::String^ GetFileExtension(System::String^ FileName) { int Ext=FileName->LastIndexOf('.'); if( Ext != -1 ) return FileName->Substring(Ext+1); return ""; }
Я бы пошел с
boost::filesystem::extensionно если вы не можете использовать 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; }дополнительные требования, которые удовлетворяются методом регулярного выражения:
- если filename как
.configили что-то вроде этого, расширение будет пустой строкой и имя файла без расширения будет.config.- если имя файла не имеет расширения, расширение будет пустой строкой,имя файла без расширения будет filename без изменений.
EDIT:
дополнительные требования также могут быть выполнены следующим образом:
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; }Примечание:
передать только имена файлов (не путь) в указанных выше функциях.
попробуйте использовать strstr
char* lastSlash; lastSlash = strstr(filename, ".");
или вы можете использовать это:
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] = ''; return Ext; } } }этот код кросс-платформенный
вот функция, которая принимает путь / имя файла в виде строки и возвращает расширение в виде строки. Это все стандартный 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 ); }
это решение, которое я придумал. Затем я заметил, что это похоже на то, что написал @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; }
С 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.
Если вы используете Poco библиотеки вы можете сделать:
#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; }
Comments