Pregunta

dada una cuerda "filename.conf", ¿Cómo verifico la parte de extensión?

Necesito una solución multiplataforma.

¿Fue útil?

Solución

Debe asegurarse de cuidar los nombres de archivos con más de un punto.ejemplo: c:\.directoryname\file.name.with.too.many.dots.ext no sería manejado correctamente por strchr o find.

Mi favorito sería el aumentar la biblioteca del sistema de archivos que tienen una función de extensión (ruta)

Otros consejos

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

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

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

Bueno, tomemos el ejemplo de strrchr, ¿qué sucede cuando usas ese método en la siguiente cadena "c:\program files\AppleGate.Net eadme"?¿Es ".Net eadme" 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.

Suponiendo que tiene 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.

Alguien más mencionó el impulso, pero yo 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

de hecho, STL puede hacer esto sin mucho código. Te aconsejo que aprendas un poco sobre STL porque te permite hacer algunas cosas sofisticadas; de todos modos, esto es lo que yo 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.a.b.c.d.e.s.mp3" si no puede encontrar la extensión, devolverá "".

En realidad, la forma más fácil es

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

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

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

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

Algunas respuestas incluían 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á como extensión el nombre completo del archivo.Para evitar eso escribí este fragmento 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;
}

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

_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Creo que esto es solo Windows (Plataforma SDK).

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

Ver también std::sistema de archivos::ruta::tronco, std::sistema de archivos::ruta::nombre de archivo.

El uso de 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 formato.

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

Para cadenas de tipo char array puedes 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.

Buenas respuestas, pero veo que la mayoría 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 archivos sin extensión pero con una ruta con un nombre de carpeta que incluye un punto, la función no podrá 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

"brian newman"La sugerencia fallará para nombre de archivo1 y nombre de archivo4.y la mayoría de las otras respuestas que se basan en la búsqueda inversa fallarán para el nombre de archivo1.Sugiero incluir el siguiente método en su fuente:que es una 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ías usar el código anterior en tu 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, una carpeta se asigna al nombre del archivo como argumento e incluye un punto en el nombre de la carpeta, la función devolverá el punto final de la carpeta, por lo que es mejor que el usuario primero verifique que el nombre dado sea un nombre de archivo y no un nombre de carpeta.

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

yo iría con boost::filesystem::extension pero si no puedes usar Boost y solo debes 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"))
{ /* ... */ }

Utilizo estas dos funciones para obtener el extensión y nombre de 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 regex enfoques 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 cumple el método regex:

  1. Si Nombre del archivo es como .config o algo como esto, extensión será una cadena vacía y nombre de archivo sin extensión será .config.
  2. Si el nombre del archivo no tiene ninguna extensión, la extensión será una cadena vacía, nombre de archivo sin extensión será el Nombre del archivo sin alterar.

EDITAR:

Los requisitos adicionales también se pueden cumplir mediante 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 archivos (no la ruta) en las funciones anteriores.

Tratar de usar strstr

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

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.

Si usa la biblioteca Qt, puede intentarlo Información de archivo Q's sufijo()

Aquí hay una función que toma una ruta/nombre de archivo como una cadena y devuelve la extensión como una cadena.Es todo 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 );
}

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

Funciona con std::string y find_last_of, pero la idea básica también funcionará si se modifica para usar char matrices y strrchr.Maneja archivos ocultos y puntos adicionales que representan el directorio actual.Es independiente de la 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;
}

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

Si por casualidad usas poco bibliotecas que puedes hacer:

#include <Poco/Path.h>

...

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

Si considera la extensión como el último punto y los posibles caracteres posteriores, pero solo si no contienen el carácter separador de directorio, la siguiente función devuelve el índice inicial de la extensión, o -1 si no se encuentra ninguna extensión.Cuando tengas eso, podrás hacer lo que quieras, 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;
}

Utilicé 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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top