Domanda

Data una stringa "filename.conf", come posso verificare la parte di estensione?

Ho bisogno di una soluzione multipiattaforma.

È stato utile?

Soluzione

Devi assicurarti di prenderti cura dei nomi dei file con più di un punto.esempio: c:\.directoryname\file.name.with.too.many.dots.ext non verrebbe gestito correttamente da strchr O find.

Il mio preferito sarebbe il potenziare la libreria del filesystem che hanno una funzione di estensione (percorso).

Altri suggerimenti

È una soluzione troppo semplice?

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

Il modo migliore è non scrivere codice che lo faccia ma chiamare metodi esistenti.In Windows, il PathFindExtension il metodo è probabilmente il più semplice.

Allora perché non dovresti scrivere il tuo?

Bene, prendi l'esempio di strrchr, cosa succede quando usi quel metodo sulla seguente stringa "c:\program files\AppleGate.Net eadme"?L'estensione è ".Net eadme"?È facile scrivere qualcosa che funzioni per alcuni casi di esempio, ma può essere molto più difficile scrivere qualcosa che funzioni per tutti i casi.

Supponendo che tu abbia accesso 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
}

Modificare: Questa è una soluzione multipiattaforma poiché non hai menzionato la piattaforma.Se utilizzi specificamente Windows, ti consigliamo di sfruttare le funzioni specifiche di Windows menzionate da altri nel thread.

Qualcun altro ha menzionato il boost ma volevo solo aggiungere il codice effettivo per farlo:

#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

in realtà STL può farlo senza molto codice, ti consiglio di imparare un po' su STL perché ti permette di fare alcune cose fantasiose, comunque questo è quello che 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 "";
}

questa soluzione restituirà sempre l'estensione anche su stringhe come "this.a.b.c.d.e.s.mp3" se non riesce a trovare l'estensione restituirà "".

In realtà, il modo più semplice è

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

Una cosa da ricordare:Se '.' non esiste nel nome file, lo sarà ext NULL.

Mi sono imbattuto in questa domanda io stesso oggi, anche se avevo già un codice funzionante, ho capito che in alcuni casi non avrebbe funzionato.

Sebbene alcune persone abbiano già suggerito di utilizzare alcune librerie esterne, preferisco scrivere il mio codice a scopo di apprendimento.

Alcune risposte includevano il metodo che stavo usando in primo luogo (cercare l'ultimo "."), ma mi sono ricordato che su Linux i file/cartelle nascosti iniziano con ".".Pertanto, se il file file è nascosto e non ha estensione, l'intero nome del file verrà preso come estensione.Per evitare ciò ho scritto questo pezzo di codice:

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

Non l'ho testato completamente, ma penso che dovrebbe funzionare.

_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Penso che questo sia solo Windows (SDK della piattaforma)?

Con C++17 e suoi std::filesystem::path::extension (la libreria è il successore di boost::filesystem) renderesti la tua affermazione più espressiva rispetto all'utilizzo ad es. 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"
    }
}

Guarda anche std::filesystem::percorso::stem, std::filesystem::percorso::nomefile.

L'uso di find/rfind di std::string risolve QUESTO problema, ma se lavori molto con i percorsi, dovresti dare un'occhiata a boost::filesystem::path poiché renderà il tuo codice molto più pulito rispetto a giocherellare con indici/iteratori di stringhe grezze.

Suggerisco boost poiché è una libreria di alta qualità, ben testata, gratuita (open source e commercialmente) e completamente portatile.

Per le stringhe di tipo array di caratteri puoi usare questo:

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

Può gestire percorsi di file oltre ai nomi di file.Funziona sia con C che con C++.E multipiattaforma.

Buone risposte ma vedo che la maggior parte di loro ha alcuni problemi:Prima di tutto penso che una buona risposta dovrebbe funzionare per nomi di file completi che hanno le intestazioni del percorso, inoltre dovrebbe funzionare per Linux o Windows o, come accennato, dovrebbe essere multipiattaforma.Per la maggior parte delle risposte;nomi di file senza estensione ma un percorso con un nome di cartella che include un punto, la funzione non riuscirà a restituire l'estensione corretta:esempi di alcuni casi di test potrebbero essere i seguenti:

    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 Newmann" il suggerimento fallirà per nomefile1 e nomefile4.e la maggior parte delle altre risposte basate sulla ricerca inversa falliranno per nomefile1.Suggerisco di includere il seguente metodo nella tua fonte:che è la funzione che restituisce l'indice del primo carattere dell'estensione o la lunghezza della stringa data se non trovata.

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

potresti utilizzare il codice sopra nella tua applicazione C++ come di seguito:

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

L'ultimo punto in alcuni casi in cui una cartella viene fornita al nome file come argomento e include un punto nel nome della cartella, la funzione restituirà il punto finale della cartella, quindi è meglio prima che l'utente controlli che il nome fornito sia un nome file e non un nome di cartella.

Una versione NET/CLI che utilizza System::String

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

Vorrei andare con boost::filesystem::extension ma se non puoi usare Boost e devi solo verificare l'estensione, una soluzione semplice è:

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

Utilizzo queste due funzioni per ottenere il file estensione E nome file senza estensione:

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 questi regex approcci per determinati requisiti aggiuntivi:

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;

}

Requisiti aggiuntivi soddisfatti dal metodo regex:

  1. Se nome del file è come .config o qualcosa del genere, estensione sarà una stringa vuota e nome file senza estensione sarà .config.
  2. Se il nome file non ha alcuna estensione, l'estensione sarà una stringa vuota, nome file senza estensione sarà il nome del file invariato.

MODIFICARE:

I requisiti aggiuntivi possono essere soddisfatti anche da quanto segue:

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:

Passa solo i nomi dei file (non il percorso) nelle funzioni precedenti.

Prova ad usare strstr

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

Oppure puoi usare questo:

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

Questo codice è multipiattaforma

Se usi la libreria Qt, puoi provarlo QFileInfo'S suffisso()

Ecco una funzione che accetta un percorso/nome file come stringa e restituisce l'estensione come stringa.È tutto C++ standard e dovrebbe funzionare multipiattaforma per la maggior parte delle piattaforme.

A differenza di molte altre risposte qui, gestisce i casi strani gestiti da PathFindExtension di Windows, in base alla documentazione di 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 );
}

Questa è una soluzione che ho trovato.Poi, ho notato che è simile a quello pubblicato da @serengeor.

Funziona con std::string E find_last_of, ma l'idea di base funzionerà anche se modificata per l'uso char array e strrchr.Gestisce file nascosti e punti extra che rappresentano la directory corrente.È indipendente dalla piattaforma.

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

Prova unitaria:

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 ti capita di usare Poco librerie che puoi fare:

#include <Poco/Path.h>

...

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

Se si considera l'estensione come l'ultimo punto e gli eventuali caratteri successivi, ma solo se non contengono il carattere separatore di directory, la funzione seguente restituisce l'indice iniziale dell'estensione, oppure -1 se non viene trovata alcuna estensione.Quando lo hai puoi fare quello che vuoi, come togliere l'estensione, cambiarla, controllarla ecc.

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

Ho utilizzato la funzione PathFindExtension() per sapere se si tratta di un file tif valido o meno.

#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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top