Question

Étant donné une chaîne "filename.conf", comment vérifier la partie extension ?

J'ai besoin d'une solution multiplateforme.

Était-ce utile?

La solution

Vous devez vous assurer de prendre soin des noms de fichiers comportant plus d’un point.exemple: c:\.directoryname\file.name.with.too.many.dots.ext ne serait pas traité correctement par strchr ou find.

Mon préféré serait le booster la bibliothèque du système de fichiers qui ont une fonction d'extension (chemin)

Autres conseils

Est-ce une solution trop 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 meilleure façon est de ne pas écrire de code qui le fasse mais d'appeler les méthodes existantes.Dans Windows, le CheminFindExtension la méthode est probablement la plus simple.

Alors pourquoi ne pas écrire le vôtre ?

Eh bien, prenons l'exemple strrchr, que se passe-t-il lorsque vous utilisez cette méthode sur la chaîne suivante « c:\program files\AppleGate.Net eadme » ?L'extension ".Net eadme" est-elle ?Il est facile d’écrire quelque chose qui fonctionne pour quelques exemples de cas, mais il peut être beaucoup plus difficile d’écrire quelque chose qui fonctionne pour tous les cas.

En supposant que vous ayez accès à 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
}

Modifier: Il s'agit d'une solution multiplateforme puisque vous n'avez pas mentionné la plateforme.Si vous utilisez spécifiquement Windows, vous souhaiterez tirer parti des fonctions spécifiques à Windows mentionnées par d'autres dans le fil de discussion.

Quelqu'un d'autre a mentionné boost mais je voulais juste ajouter le code réel pour ce faire :

#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 fait, la STL peut faire cela sans trop de code, je vous conseille d'en apprendre un peu plus sur la STL car elle vous permet de faire des choses sophistiquées, de toute façon c'est ce que j'utilise.

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

cette solution renverra toujours l'extension même sur des chaînes comme "this.a.b.c.d.e.s.mp3" si elle ne trouve pas l'extension, elle renverra "".

En fait, le moyen le plus simple est

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

Une chose à retenir :si '.' n'existe pas dans le nom de fichier, ext sera NULL.

Je suis moi-même tombé sur cette question aujourd'hui, même si j'avais déjà un code fonctionnel, j'ai compris que cela ne fonctionnerait pas dans certains cas.

Alors que certaines personnes ont déjà suggéré d'utiliser des bibliothèques externes, je préfère écrire mon propre code à des fins d'apprentissage.

Certaines réponses incluaient la méthode que j'utilisais en premier lieu (rechercher le dernier "."), mais je me suis souvenu que sous Linux, les fichiers/dossiers cachés commençaient par ".".Ainsi, si le fichier est caché et n’a pas d’extension, le nom complet du fichier sera pris pour extension.Pour éviter cela, j'ai écrit ce morceau de code :

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

Je n'ai pas testé cela complètement, mais je pense que cela devrait fonctionner.

_splitpath, _wsplitpath, _splitpath_s, _wsplitpath_w

Je pense qu'il s'agit uniquement de Windows (Platform SDK) ?

Avec C++17 et ses std::filesystem::path::extension (la bibliothèque est le successeur de boost::filesystem), vous rendriez votre déclaration plus expressive que d'utiliser par ex. 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"
    }
}

Voir également std :: système de fichiers :: chemin :: tige, std :: système de fichiers :: chemin :: nom de fichier.

L'utilisation de std::string's find/rfind résout CE problème, mais si vous travaillez beaucoup avec des chemins, vous devriez regarder boost::filesystem::path car cela rendra votre code beaucoup plus propre que de jouer avec des index/itérateurs de chaînes brutes.

Je suggère boost car il s'agit d'une bibliothèque de haute qualité, bien testée, (open source et commerciale) gratuite et entièrement portable.

Pour les chaînes de type tableau de caractères, vous pouvez utiliser ceci :

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

Peut gérer les chemins de fichiers en plus des noms de fichiers.Fonctionne avec C et C++.Et multiplateforme.

Bonnes réponses mais je vois que la plupart d'entre elles ont quelques problèmes :Tout d'abord, je pense qu'une bonne réponse devrait fonctionner pour les noms de fichiers complets qui ont leurs en-têtes de chemin, elle devrait également fonctionner pour Linux ou Windows ou, comme mentionné, elle devrait être multiplateforme.Pour la plupart des réponses ;noms de fichiers sans extension mais un chemin avec un nom de dossier incluant un point, la fonction ne parviendra pas à renvoyer l'extension correcte :des exemples de cas de test pourraient être les suivants :

    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 suggestion échouera pour filename1 et filename4.et la plupart des autres réponses basées sur la recherche inversée échoueront pour filename1.Je suggère d'inclure la méthode suivante dans votre source :qui est une fonction renvoyant l'index du premier caractère de l'extension ou la longueur de la chaîne donnée si elle n'est pas trouvée.

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

vous pouvez utiliser le code ci-dessus dans votre application C++ comme ci-dessous :

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

Le dernier point dans certains cas, un dossier est donné au nom de fichier comme argument et inclut un point dans le nom du dossier. La fonction renverra le point final du dossier, il est donc préférable de vérifier d'abord que le nom donné est un nom de fichier et non un nom de dossier.

Une version NET/CLI utilisant System::String

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

j'irais avec boost::filesystem::extension mais si vous ne pouvez pas utiliser Boost et qu'il vous suffit de vérifier l'extension, une solution simple est :

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

J'utilise ces deux fonctions pour obtenir le extension et nom de fichier sans extension:

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

Et ceux-ci regex approches pour certaines exigences supplémentaires :

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;

}

Exigences supplémentaires satisfaites par la méthode regex :

  1. Si nom de fichier est comme .config ou quelque chose comme ça, extension sera une chaîne vide et nom de fichier sans extension sera .config.
  2. Si le nom de fichier n'a pas d'extension, l'extension sera une chaîne vide, nom de fichier sans extension sera le nom de fichier inchangé.

MODIFIER:

Les exigences supplémentaires peuvent également être satisfaites par les éléments suivants :

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

Note:

Transmettez uniquement les noms de fichiers (pas le chemin) dans les fonctions ci-dessus.

Essayez d'utiliser strstr

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

Ou vous pouvez utiliser ceci :

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

Ce code est multiplateforme

Si vous utilisez la bibliothèque Qt, vous pouvez essayer QFileInfoc'est suffixe()

Voici une fonction qui prend un chemin/nom de fichier sous forme de chaîne et renvoie l'extension sous forme de chaîne.Tout cela est du C++ standard et devrait fonctionner sur plusieurs plates-formes pour la plupart des plates-formes.

Contrairement à plusieurs autres réponses ici, il gère les cas étranges gérés par PathFindExtension de Windows, sur la base de la documentation 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 );
}

C'est une solution que j'ai trouvée.Ensuite, j'ai remarqué que c'est similaire à ce que @serengeor a publié.

Cela fonctionne avec std::string et find_last_of, mais l'idée de base fonctionnera également si elle est modifiée pour utiliser char tableaux et strrchr.Il gère les fichiers cachés et les points supplémentaires représentant le répertoire actuel.Il est indépendant de la plateforme.

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

Test de l'unité:

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

S'il vous arrive d'utiliser Poco bibliothèques, vous pouvez faire :

#include <Poco/Path.h>

...

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

Si vous considérez l'extension comme le dernier point et les caractères possibles qui le suivent, mais seulement s'ils ne contiennent pas le caractère séparateur de répertoire, la fonction suivante renvoie l'index de départ de l'extension, ou -1 si aucune extension n'est trouvée.Lorsque vous avez cela, vous pouvez faire ce que vous voulez, comme supprimer l'extension, la modifier, la vérifier, 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;
}

J'ai utilisé la fonction PathFindExtension() pour savoir s'il s'agit d'un fichier tif valide ou non.

#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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top