Учитывая строку "filename.conf"
, как мне проверить часть расширения?
Мне нужно кроссплатформенное решение.
Ответы:
Вы должны позаботиться о том, чтобы имена файлов состояли более чем из одной точки. пример: c:\.directoryname\file.name.with.too.many.dots.ext
не будет правильно обработан strchr
илиfind.
Мне больше всего нравится библиотека файловой системы boost с функцией расширения (пути).
Это слишком простое решение?
#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;
}
}
return "Yes...";
без проверки - это подразумевает, что решение должно работать для других входов. В качестве другого примера счетчика файл с именем просто «conf» без расширения также вернет «Да ...» с учетом вышеизложенного.
boost::filesystem
, что тривиально в использовании, но обеспечивает необходимую поддержку. См. Boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm
Лучший способ - не писать код, который это делает, а вызывать существующие методы. В Windows метод 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
}
Изменить: это кроссплатформенное решение, поскольку вы не упомянули платформу. Если вы специально работаете с Windows, вы захотите использовать специальные функции Windows, упомянутые другими в потоке.
Кто-то еще упомянул повышение, но я просто хотел добавить для этого код:
#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.abcdesmp3", если оно не может найти расширение, которое вернет "".
С 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 .
Я сегодня сам наткнулся на этот вопрос, хотя у меня уже был рабочий код, я понял, что в некоторых случаях он не будет работать.
Хотя некоторые люди уже предлагали использовать некоторые внешние библиотеки, я предпочитаю писать свой собственный код в учебных целях.
Некоторые ответы включали метод, который я использовал в первую очередь (поиск последнего "."), Но я вспомнил, что в 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;
}
Я не тестировал это полностью, но думаю, что это должно работать.
Использование std :: string find / rfind решает ЭТУ проблему, но если вы много работаете с путями, вам следует взглянуть на boost :: filesystem :: path, поскольку это сделает ваш код намного чище, чем возиться с необработанными строковыми индексами / итераторами.
Я предлагаю boost, поскольку это высококачественная, хорошо протестированная (с открытым исходным кодом и коммерчески) бесплатная и полностью переносимая библиотека.
Для строк типа массива 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
цикл возвращает истину.
Хорошие ответы, но я вижу, что у большинства из них есть некоторые проблемы: Прежде всего, я думаю, что хороший ответ должен работать для полных имен файлов, у которых есть заголовки пути, также он должен работать для 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));
}
Последняя точка в некоторых случаях, когда папка дается имени файла в качестве аргумента и включает точку в имени папки, функция возвращает конечную точку папки, поэтому лучше сначала пользователю проверить, является ли данное имя именем файла, а не именем папки.
Версия 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
( 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"))
{ /* ... */ }
Это решение, которое я придумал. Затем я заметил, что это похоже на то, что опубликовал @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;
}
Я использую эти две функции, чтобы получить расширение и имя файла без расширения :
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;
}
Дополнительные требования, которым отвечает метод регулярного выражения:
.config
или что-то в этом роде, расширение будет пустой строкой, а имя файла без расширения будет .config
.РЕДАКТИРОВАТЬ:
Дополнительные требования также могут быть выполнены за счет следующего:
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;
}
Заметка:
Передайте только имена файлов (не путь) в вышеуказанных функциях.
Или вы можете использовать это:
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 «s суффикса ()
Вот функция, которая принимает путь / имя файла в виде строки и возвращает расширение в виде строки. Все это стандартный 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
максимально возможным целым числом без знака. Так что может показаться, что папки нет, даже если она есть!
Если вы используете библиотеки 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;
}
Вы можете использовать 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;
}