Dada una cadena "filename.conf"
, ¿cómo verifico la parte de extensión?
Necesito una solución multiplataforma.
Respuestas:
Debe asegurarse de cuidar los nombres de archivo con más de un punto. ejemplo: c:\.directoryname\file.name.with.too.many.dots.ext
no sería manejado correctamente por strchr
ofind.
Mi favorita sería la biblioteca del sistema de archivos boost que tiene una función de extensión (ruta)
¿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;
}
}
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.
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
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.
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.
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
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á "".
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 .
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
.
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.
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.
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.
strlen(extension)
en for
condiciones. Entonces, si los caracteres no coinciden, devuelve falso. El for
bucle exterior devuelve verdadero.
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.
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
( std::filesystem::path::extension
con 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"))
{ /* ... */ }
_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w
Esto es solo para Windows (Platform SDK)
Esta es una solución que se me ocurrió. Entonces, noté que es similar a lo que publicó @serengeor.
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 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;
}
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 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 se cumplen con el método regex:
.config
o algo así, la extensión será una cadena vacía y el nombre del archivo sin extensión será .config
.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.
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
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 );
}
max( filename.rfind(L'\\'), filename.rfind(L'/') )
compararé dos valores sin signo, uno de ellos podría ser npos
cuál es el entero sin signo más grande posible. ¡Por lo tanto, puede parecer que no hay carpeta incluso cuando está allí!
Si utiliza bibliotecas de Poco , puede 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 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;
}
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;
}
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;
}