Cómo obtener la extensión de archivo de una cadena en C ++

Respuestas:

34

Debe asegurarse de cuidar los nombres de archivo con más de un punto. ejemplo: c:\.directoryname\file.name.with.too.many.dots.extno sería manejado correctamente por strchrofind.

Mi favorita sería la biblioteca del sistema de archivos boost que tiene una función de extensión (ruta)

Thomas Bonini
fuente
12
Sin embargo, el nombre de su directorio se maneja fácilmente mediante búsqueda inversa :).
17 de 26 de
30
En mi opinión personal, las soluciones boost no deberían aparecer como respuestas a problemas de C ++. Requerir una biblioteca externa para algo tan simple parece un poco tonto.
pantano
4
@marsh: sin embargo, el problema tan simple tiene sus casos especiales, especialmente cuando se trata de sistemas de archivos, un concepto para el que casi todos los sistemas operativos principales (y no tan importantes) tienen su propia interpretación. Considere, por ejemplo, archivos ocultos de Linux (`/home/oren/.conf '), o el caso mencionado por @Torlack . @ 17 de 26, tratar de mencionar solo su nombre de usuario debería resaltar los problemas que pueden surgir al simplificar demasiado la forma en que las personas usan los nombres de forma libre;)
Oren S
@OrenS No obstante, la solución boost no debe aceptarse nunca como la respuesta a una pregunta que no pregunte cómo hacerlo con boost. Es engañoso.
Silidrone
@MuhamedCicak ... bueno, la solución portátil para othervise implica un largo fragmento de código que tiene en cuenta la codificación de los nombres de archivo y / o usa otras bibliotecas (sospecho que boost no lo implementa desde cero, en su lugar usa otros paquetes o API donde posible). Tenga en cuenta que incluso obtener la ruta canónica del parcial uno como tarea es un gran problema con media docena de casos extremos ...
Swift - Friday Pie
156

¿Es esta una solución demasiado simple?

#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;
  }
}
Brian Newman
fuente
12
@Qué sucede cuando el nombre del archivo no tiene extensión y la carpeta anterior tiene. en su nombre?
Mircea Ispas
4
Estoy respondiendo la pregunta; que especifica "filename.conf", no su hipotético.
Brian Newman
5
Según esa lógica, podría decir return "Yes...";sin ningún control: está implícito que la solución debería funcionar para otras entradas. Como otro ejemplo contrario, un archivo llamado simplemente "conf" sin extensión también devolvería "Sí ..." dado lo anterior.
Rollie
4
Advertencia para otros: Esta es una solución demasiado simple para ser utilizada en el código de producción, excepto para proyectos específicos y estrechos que no necesitan manejar una variedad de escenarios de usuarios finales del mundo real. El análisis y el manejo de nombres de archivos no son triviales. Yo personalmente casi siempre uso boost::filesystem, que es trivial de usar, pero proporciona el soporte necesario. Ver boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm
Dan Nissenbaum
1
std :: filesystem :: path :: extension ahora es parte del estándar, verifique, por ejemplo, la respuesta de Roi Danton a continuación.
yves
42

La mejor manera es no escribir ningún código que lo haga, sino llamar a métodos existentes. En Windows, el método PathFindExtension es probablemente el más simple.

Entonces, ¿por qué no escribirías el tuyo propio?

Bueno, tome el ejemplo strrchr, ¿qué sucede cuando usa ese método en la siguiente cadena "c: \ archivos de programa \ AppleGate.Net \ readme"? ¿Es ".Net \ readme" la extensión? Es fácil escribir algo que funcione para algunos casos de ejemplo, pero puede ser mucho más difícil escribir algo que funcione para todos los casos.

Torlack
fuente
3
+1 ¡No escribir ningún código nuevo es a menudo la mejor respuesta! La versión C # de esto era lo que buscaba hace un momento, pero tu respuesta me llevó allí. msdn.microsoft.com/en-us/library/…
Tom Resing
Esta función (en Windows 7) no manejará correctamente "file.i i". Sí, esto es válido, fíjate en el espacio.
pcunite
Ha preguntado acerca de cómo recuperar la extensión de un archivo, no una ruta completa. Además, una función API de Windows no sería una buena respuesta. Esto no es en absoluto una respuesta, sino un comentario.
Didac Perez Parera
4
-1 para proporcionar una solución específica de plataforma cuando el OP solicitó una solución portátil.
jb
+1 de mi parte. Esta pregunta es la primera que surge cuando busca en Google 'mfc get file extension', y la suya es la respuesta más simple que funciona.
Eternal21 de
32

Suponiendo que tenga acceso a 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 es una solución multiplataforma ya que no mencionaste la plataforma. Si está específicamente en Windows, querrá aprovechar las funciones específicas de Windows mencionadas por otros en el hilo.

17 de 26
fuente
6
+1, esta es la solución más simple en caso de que tenga un archivo en una cadena y no una ruta.
Thomas Bonini
25

Alguien más mencionó boost, pero solo quería agregar el código real para hacer esto:

#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
peter karasev
fuente
20

en realidad, el STL puede hacer esto sin mucho código, le aconsejo que aprenda un poco sobre el STL porque le permite hacer algunas cosas elegantes, de todos modos esto es lo que 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 solución siempre devolverá la extensión incluso en cadenas como "this.abcdesmp3" si no puede encontrar la extensión, devolverá "".

maestro de grafito
fuente
15

Con C ++ 17 y su std::filesystem::path::extension(la biblioteca es la sucesora de boost :: filesystem) haría su declaración más expresiva que usando 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"
    }
}

Vea también std :: filesystem :: path :: stem , std :: filesystem :: path :: filename .

Roi Danton
fuente
7

En realidad, la forma más sencilla es

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

Una cosa para recordar: si '.'no existe en el nombre del archivo, ext será NULL.

Qiu
fuente
4
Esta no sería la solución perfecta para los archivos ocultos de UNIX que comienzan con un punto
Mark Kahn
¿debería ser const char * ext?
Vlad
4

Yo mismo me encontré con esta pregunta hoy, aunque ya tenía un código de trabajo, me di cuenta de que no funcionaría en algunos casos.

Si bien algunas personas ya sugirieron usar algunas bibliotecas externas, prefiero escribir mi propio código con fines de aprendizaje.

Algunas respuestas incluyeron el método que estaba usando en primer lugar (buscando el último "."), Pero recordé que en Linux los archivos / carpetas ocultos comienzan con ".". Entonces, si el archivo está oculto y no tiene extensión, se tomará el nombre completo del archivo como extensión. Para evitar eso, escribí este 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;
}

No lo he probado completamente, pero creo que debería funcionar.

serengeor
fuente
3

Usar std :: string's find / rfind resuelve ESTE problema, pero si trabaja mucho con rutas, entonces debería mirar boost :: filesystem :: path ya que hará que su código sea mucho más limpio que jugar con índices / iteradores de cadenas sin procesar.

Sugiero boost ya que es una biblioteca de alta calidad, bien probada, (de código abierto y comercial) gratuita y totalmente portátil.

KristianR
fuente
3

Para cadenas de tipo matriz de caracteres, puede usar esto:

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

Puede manejar rutas de archivos además de nombres de archivos. Funciona tanto con C como con C ++. Y multiplataforma.

delaccount992
fuente
Podría disminuir el número de condiciones. Usar strlen(extension)en forcondiciones. Entonces, si los caracteres no coinciden, devuelve falso. El forbucle exterior devuelve verdadero.
LRDPRDX
3

Buenas respuestas, pero veo que la mayoría de ellas tiene algunos problemas: en primer lugar, creo que una buena respuesta debería funcionar para nombres de archivos completos que tienen sus encabezados de ruta, también debería funcionar para Linux o Windows o, como se mencionó, debería ser multiplataforma. Para la mayoría de las respuestas; nombres de archivo sin extensión, pero una ruta con un nombre de carpeta que incluye un punto, la función no devolverá la extensión correcta: ejemplos de algunos casos de prueba podrían ser los siguientes:

    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

La sugerencia de " brian newman " fallará para filename1 y filename4. y la mayoría de las otras respuestas que se basan en la búsqueda inversa fallarán para filename1. Sugiero incluir el siguiente método en su fuente: que es la función que devuelve el índice del primer carácter de la extensión o la longitud de la cadena dada si no se encuentra.

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

podría usar el código anterior en su aplicación c ++ como se muestra a continuación:

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

El último punto en algunos casos es que se le da una carpeta al nombre del archivo como argumento e incluye un punto en el nombre de la carpeta, la función devolverá el punto de la carpeta al final, así que es mejor que el usuario compruebe primero que el nombre dado es un nombre de archivo y no un nombre de carpeta.

AMCoded
fuente
3

Una versión NET / CLI usando System :: String

   System::String^ GetFileExtension(System::String^ FileName)
   {
       int Ext=FileName->LastIndexOf('.');
       if( Ext != -1 )
           return FileName->Substring(Ext+1);
       return "";
   }
Leopoldo Sanczyk
fuente
Esto no es Visual C ++, es .NET / CLI .
Victor
1
@Victor Edité la respuesta. Gracias por la aclaración.
Leopoldo Sanczyk
3

Yo iría con boost::filesystem::extension( std::filesystem::path::extensioncon C ++ 17) pero si no puede usar Boost y solo tiene que verificar la extensión, una solución simple es:

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"))
{ /* ... */ }
manlio
fuente
3
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Esto es solo para Windows (Platform SDK)

Cerdo hormiguero
fuente
2

Esta es una solución que se me ocurrió. Entonces, noté que es similar a lo que publicó @serengeor.

Funciona con std::stringy find_last_of, pero la idea básica también funcionará si se modifica para usar charmatrices y strrchr. Maneja archivos ocultos y puntos adicionales que representan el directorio actual. Es una plataforma independiente.

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

Prueba de unidad:

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;
}
Mike Finch
fuente
2

Utilizo estas dos funciones para obtener la extensión y el nombre del archivo sin extensión :

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

Y estos regexenfoques para ciertos requisitos adicionales:

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 adicionales que se cumplen con el método regex:

  1. Si el nombre del archivo es similar .configo algo así, la extensión será una cadena vacía y el nombre del archivo sin extensión será .config.
  2. Si el nombre de archivo no tiene ninguna extensión, la extensión será una cadena vacía, el nombre de archivo sin extensión será el nombre de archivo sin cambios.

EDITAR:

Los requisitos adicionales también se pueden cumplir con lo siguiente:

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

Nota:

Pase solo los nombres de archivo (no la ruta) en las funciones anteriores.

Jahid
fuente
1

Intenta usar strstr

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

O puedes usar esto:

    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 es multiplataforma

Búsqueda
fuente
1

Si usa la biblioteca Qt, puede probar el sufijo de QFileInfo ()

Mark Kahn
fuente
2
¿Qué tiene que ver Qt con esta pregunta? ¿Por qué introducir una gran dependencia de terceros para una simple manipulación de cadenas? Si va por esa ruta, ¿por qué no usar boost?
derpface
1

Aquí hay una función que toma una ruta / nombre de archivo como una cadena y devuelve la extensión como una cadena. Todo es C ++ estándar y debería funcionar multiplataforma para la mayoría de las plataformas.

A diferencia de varias otras respuestas aquí, maneja los casos extraños que maneja PathFindExtension de Windows, según la documentación de 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 );
}
tfinniga
fuente
Hola, hay un problema con su solución: max( filename.rfind(L'\\'), filename.rfind(L'/') )compararé dos valores sin signo, uno de ellos podría ser nposcuál es el entero sin signo más grande posible. ¡Por lo tanto, puede parecer que no hay carpeta incluso cuando está allí!
Andrii Kovalevskyi
0

Si utiliza bibliotecas de Poco , puede hacer:

#include <Poco/Path.h>

...

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

Si considera la extensión como el último punto y los posibles caracteres después, pero solo si no contienen el carácter separador de directorio, la siguiente función devuelve el índice de inicio de la extensión, o -1 si no se encuentra la extensión. Cuando tenga eso, puede hacer lo que quiera, como quitar la extensión, cambiarla, verificarla, 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;
}
Yuval
fuente
0

Usé la función PathFindExtension () para saber si es un archivo tif válido o no.

#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;
}
Pabitra Dash
fuente
0

Puede usar strrchr () para encontrar la última aparición de. (Dot) y obtener archivos de extensiones basados ​​en. (Dot). Verifique el siguiente código, por ejemplo.

#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;
}
HaseeB Mir
fuente