Pergunta

Dada uma string "filename.conf", como posso verificar a parte de extensão?

Preciso de uma solução multiplataforma.

Foi útil?

Solução

Você deve cuidar dos nomes dos arquivos com mais de um ponto.exemplo: c:\.directoryname\file.name.with.too.many.dots.ext não seria tratado corretamente por strchr ou find.

Meu favorito seria o aumentar a biblioteca do sistema de arquivos que possuem uma função de extensão (caminho)

Outras dicas

Esta é uma solução muito simples?

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

A melhor maneira é não escrever nenhum código que faça isso, mas chamar métodos existentes.Nas janelas, o Extensão PathFind método é provavelmente o mais simples.

Então, por que você não escreveria o seu próprio?

Bem, veja o exemplo strrchr, o que acontece quando você usa esse método na seguinte string "c:\program files\AppleGate.Net eadme"?".Net eadme" é a extensão?É fácil escrever algo que funcione para alguns casos de exemplo, mas pode ser muito mais difícil escrever algo que funcione para todos os casos.

Supondo que você tenha acesso ao 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
}

Editar: Esta é uma solução multiplataforma, já que você não mencionou a plataforma.Se você estiver especificamente no Windows, você desejará aproveitar as funções específicas do Windows mencionadas por outras pessoas no tópico.

Alguém mencionou boost, mas eu só queria adicionar o código real para fazer isso:

#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

na verdade o STL pode fazer isso sem muito código, aconselho você a aprender um pouco sobre o STL porque ele permite fazer algumas coisas sofisticadas, de qualquer forma é isso que eu uso.

std::string GetFileExtension(const std::string& FileName)
{
    if(FileName.find_last_of(".") != std::string::npos)
        return FileName.substr(FileName.find_last_of(".")+1);
    return "";
}

esta solução sempre retornará a extensão mesmo em strings como "this.a.b.c.d.e.s.mp3" se não conseguir encontrar a extensão, retornará "".

Na verdade, a maneira mais fácil é

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

Uma coisa para lembrar:se '.' não existe no nome do arquivo, ext será NULL.

Eu mesmo me deparei com essa questão hoje, embora já tivesse um código funcionando, descobri que não funcionaria em alguns casos.

Embora algumas pessoas já tenham sugerido o uso de algumas bibliotecas externas, prefiro escrever meu próprio código para fins de aprendizagem.

Algumas respostas incluíam o método que eu estava usando em primeiro lugar (procurando o último "."), mas lembrei que no Linux os arquivos/pastas ocultos começam com ".".Portanto, se o arquivo estiver oculto e não tiver extensão, todo o nome do arquivo será considerado extensão.Para evitar isso, escrevi este trecho de código:

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

Não testei isso completamente, mas acho que deve funcionar.

_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Acho que é apenas Windows (Platform SDK)?

Com C++17 e seus std::filesystem::path::extension (a biblioteca é a sucessora de boost::filesystem) você tornaria sua declaração mais expressiva do que usar, por exemplo. 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"
    }
}

Veja também std::filesystem::path::stem, std::sistema de arquivos::caminho::nome do arquivo.

Usar o find/rfind de std::string resolve ESTE problema, mas se você trabalha muito com caminhos, então você deve olhar boost::filesystem::path pois isso tornará seu código muito mais limpo do que mexer com índices/iteradores de string brutos.

Sugiro boost, pois é uma biblioteca de alta qualidade, bem testada, (de código aberto e comercialmente) gratuita e totalmente portátil.

Para strings do tipo array char você pode usar isto:

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

Pode lidar com caminhos de arquivos além de nomes de arquivos.Funciona com C e C++.E multiplataforma.

Boas respostas, mas vejo que a maioria delas tem alguns problemas:Primeiro de tudo, acho que uma boa resposta deve funcionar para nomes de arquivos completos que possuem seus cabeçalhos de caminho, também deve funcionar para Linux ou Windows ou, como mencionado, deve ser multiplataforma.Para a maioria das respostas;nomes de arquivo sem extensão, mas um caminho com um nome de pasta incluindo ponto, a função não retornará a extensão correta:exemplos de alguns casos de teste poderiam ser os seguintes:

    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

"Brian Newman"a sugestão falhará para filename1 e filename4.e a maioria das outras respostas baseadas na localização reversa falharão no nome do arquivo1.Sugiro incluir o seguinte método em sua fonte:que é uma função que retorna o índice do primeiro caractere da extensão ou o comprimento de uma determinada string, se não for encontrada.

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

você poderia usar o código acima em seu aplicativo c++ como abaixo:

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

O último ponto, em alguns casos, a pasta é dada ao nome do arquivo como argumento e inclui um ponto no nome da pasta, a função retornará o ponto final da pasta, então é melhor primeiro o usuário verificar se o nome fornecido é um nome de arquivo e não um nome de pasta.

Uma versão NET/CLI usando System::String

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

eu iria com boost::filesystem::extension mas se você não puder usar o Boost e precisar apenas verificar a extensão, uma solução simples é:

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

Eu uso essas duas funções para obter o extensão e nome de arquivo sem extensão:

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

E esses regex abordagens para certos requisitos extras:

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;

}

Requisitos extras atendidos pelo método regex:

  1. Se nome do arquivo é como .config ou algo parecido com isto, extensão será uma string vazia e nome de arquivo sem extensão vai ser .config.
  2. Se o nome do arquivo não tiver nenhuma extensão, a extensão será uma string vazia, nome de arquivo sem extensão será o nome do arquivo inalterado.

EDITAR:

Os requisitos extras também podem ser atendidos pelo seguinte:

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

Observação:

Passe apenas os nomes dos arquivos (não o caminho) nas funções acima.

Tente usar strstr

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

Ou você pode usar isto:

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

Este código é multiplataforma

Se você usa a biblioteca Qt, você pode tentar QFileInfode sufixo()

Aqui está uma função que recebe um caminho/nome de arquivo como uma string e retorna a extensão como uma string.É tudo C++ padrão e deve funcionar em várias plataformas para a maioria das plataformas.

Ao contrário de várias outras respostas aqui, ele lida com os casos estranhos que o PathFindExtension do Windows lida, com base na documentação do 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 );
}

Esta é uma solução que eu encontrei.Aí percebi que é parecido com o que @serengeor postou.

Funciona com std::string e find_last_of, mas a ideia básica também funcionará se for modificada para usar char matrizes e strrchr.Ele lida com arquivos ocultos e pontos extras que representam o diretório atual.É independente de plataforma.

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

Teste de unidade:

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

Se acontecer de você usar Poco bibliotecas que você pode fazer:

#include <Poco/Path.h>

...

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

Se você considerar a extensão como o último ponto e os possíveis caracteres depois dele, mas somente se eles não contiverem o caractere separador de diretório, a função a seguir retornará o índice inicial da extensão ou -1 se nenhuma extensão for encontrada.Quando você tiver isso, você pode fazer o que quiser, como retirar a extensão, alterá-la, verificá-la etc.

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

Usei a função PathFindExtension() para saber se é um arquivo tif válido ou não.

#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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top