如何在C ++中从字符串获取文件扩展名


Answers:


34

您必须确保使用多于一个点的文件名。范例:或c:\.directoryname\file.name.with.too.many.dots.ext无法正确处理strchrfind.

我最喜欢的是具有扩展功能(路径)的boost文件系统库


12
您的目录名称很容易通过反向查找来处理:)。

30
我个人认为boost解决方案不应被列为C ++问题的答案。需要外部库提供如此简单的内容似乎有点愚蠢。
2015年

4
@marsh:但是,如此简单的问题有其特殊情况,尤其是在处理文件系统时-几乎每个主要(而非主要)操作系统都有其自己的解释的概念。考虑例如linux隐藏文件(`/home/oren/.conf'),或@Torlack提到的情况。@ 26之17,试图仅提及您的用户名应突出显示可能由于过度简化人们使用自由格式命名方式而引起的问题;)
Oren S

但是,@ OrenS不应将boost解决方案作为不问如何使用boost的问题的答案。这是误导。
Silidrone

@MuhamedCicak ...好吧,其他解决方案的可移植解决方案涉及一些较长的代码,这些代码考虑了文件名的编码或/和使用了其他库(我怀疑boost没有从头开始实现它,而是使用了其他软件包或API,可能)。请注意,即使是从部分任务中获得规范路径,也是数十种边缘情况的一个巨大问题……
Swift-Friday Pie

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
@当文件名没有扩展名并且上一个文件夹有时会发生什么。以它的名字?
Mircea Ispas

4
我在回答这个问题;它指定“ filename.conf”,而不是您的假设。
布赖恩·纽曼

5
按照这种逻辑,您可以说return "Yes...";根本不做检查-这意味着该解决方案应该适用于其他输入。作为另一个反例,给定上述名称,没有扩展名的仅名为“ conf”的文件也将返回“ Yes ...”。
Rollie

4
对其他人的警告:这对于在生产代码中使用的解决方案来说太简单了,除了不需要处理各种现实世界最终用户场景的狭窄而特定的项目之外。文件名的解析和处理是不平凡的。我个人几乎总是使用boost::filesystem,虽然使用起来很简单,但是却提供了必要的支持。参见boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm
Dan Nissenbaum 2014年

1
std :: filesystem ::: path :: extension现在已成为标准的一部分,请检查下面的Roi Danton答案。
yves

42

最好的方法是不编写任何执行此操作的代码,而是调用现有方法。在Windows中,PathFindExtension方法可能是最简单的。

那为什么不写自己的呢?

好吧,以strrchr为例,在以下字符串“ c:\ program files \ AppleGate.Net \ readme”上使用该方法会发生什么?是“ .Net \ readme”的扩展名吗?编写适用于一些示例案例的东西很容易,但是编写适用于所有案例的东西要困难得多。


3
+1不编写新代码通常是最好的答案!C#版本就是我刚刚才想到的,但是您的回答使我到了那里。msdn.microsoft.com/en-us/library/...
汤姆Resing

此功能(在Windows 7下)将无法正确处理“ file.i i”。是的,这是有效的,请注意空格。
pcunite

他询问有关从文件而不是完整路径中检索扩展名的问题。另外,Windows API函数不是一个好的答案。这绝对不是答案,而是评论。
Didac Perez Parera

4
-1,用于在OP请求便携式解决方案时提供特定于平台的解决方案。
2014年

从我+1。这个问题是当您使用Google“ 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 2010年

25

有人提到了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

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的后继程序),您的语句将比使用eg更具表达力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 :: stemstd :: filesystem :: path :: filename


7

其实最简单的方法是

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

要记住的一件事:如果'.'文件名中不存在ext,则它将为NULL


4
对于以点开头的UNIX隐藏文件来说,这不是完美的解决方案
Mark Kahn 2014年

应该是const char * ext吗?
弗拉德(Vlad)

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条件。然后,如果字符不匹配,则返回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

对于filename1和filename4,“ brian newman ”建议将失败。而其他大多数基于反向查找的答案将对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));
}

在某些情况下,最后一点是将文件夹指定为文件名作为参数,并且在文件夹名中包含一个点,该函数将返回文件夹的点尾,因此最好首先检查用户给定名称是文件名而不是文件夹名。


3

使用System :: String的NET / CLI版本

   System::String^ GetFileExtension(System::String^ FileName)
   {
       int Ext=FileName->LastIndexOf('.');
       if( Ext != -1 )
           return FileName->Substring(Ext+1);
       return "";
   }

这不是Visual C ++,而是.NET / CLI
胜者

1
@Victor我编辑了答案。感谢您的澄清。
莱奥波尔多Sanczyk

3

我会使用boost::filesystem::extensionstd::filesystem::path::extensionC ++ 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(Platform 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;

}

regex方法可以满足的其他要求:

  1. 如果文件名是像.config或这样的事情,扩展将是一个空字符串和文件名不带扩展.config
  2. 如果文件名没有任何扩展,extention将是一个空字符串,没有扩展名的文件名会是文件名不变。

编辑:

还可以通过以下方式满足额外的要求:

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

这是一个将路径/文件名作为字符串并以字符串形式返回扩展名的函数。它都是标准的c ++,并且可以在大多数平台上跨平台工作。

与这里的其他几个答案不同,它根据PathFindExtensions的文档处理Windows的PathFindExtension处理的奇怪情况。

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 2015年


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()查找..dot的最后一次出现,并获取基于。(dot)的扩展文件。例如,检查以下代码。

#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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.