J'ai un nom de fichier std::wstring fName pour lequel je voudrais tester s'il a une extension .txt. Cela fonctionne:

return ((fName.length() >= 4) && (0 == fName.compare(fName.length() - 4, 4, L".txt")));

Mais il est sensible à la casse, ce que je ne veux pas: il faut que blah.tXt et hello.TXT soient tous les deux acceptés.


Cela devrait fonctionner comme une version insensible à la casse:

std::wstring ext = L".txt";
wstring::const_iterator it = std::search(fName.end() - 4, fName.end(), ext.begin(), ext.end(), 
                               [](wchar_t ch1, wchar_t ch2) { return tolower(ch1) == ch2; }); 
                    // no need tolower(ch2) because the pattern .txt is already lowercase
return (it != str1.end());

Mais le std::search est probablement loin d'être optimal car il cherche s'il contient un motif (n'importe où dans la chaîne d'origine), et ici je n'ai besoin que de comparer caractère par caractère.


Comme je dois tester cela pour des millions de noms de fichiers, comment puis-je améliorer les performances pour vérifier si un nom de fichier a une extension (non sensible à la casse) .txt?

Je ne veux pas de la solution facile:

  • minuscules fName dans une nouvelle variable (ou même minuscules juste les 4 derniers caractères de fName)

  • puis comparez

Car cela nécessiterait de nouvelles variables, de la mémoire, etc. Puis-je comparer en place , avec un prédicat personnalisé [](wchar_t ch1, wchar_t ch2) { return tolower(ch1) == ch2; })?


Remarque: je ne suis pas à la recherche de solutions Boost, ni de solutions comme celle-ci Comparaison de chaînes insensible à la casse en C ++ ou de nombreuses questions similaires qui ne sont pas optimisées pour les performances.

2
Basj 20 juil. 2017 à 02:59

4 réponses

Meilleure réponse

Comme suggéré dans le commentaire de @ fghj, c'est une bonne solution:

std::equal(fName.end() - ext.length(), fName.end(), ext.begin(),
           [](wchar_t ch1, wchar_t ch2) { return tolower(ch1) == ch2; });
0
Basj 25 juil. 2017 à 21:37

Si vous voulez une implémentation sans hypothèses (cela ne suppose pas non plus la longueur de l'extension, mais suppose que le fichier a un nom d'au moins 4 caractères de taille):

char * testing = &fName[fName.length() - 4];
unsigned int index = 1;
unsigned int total = 0;
while(index < 4) {
    total += testing[index] << index;
    ++index;
}
return total == ('t' << 1) + ('x' << 2) + ('t' << 3) || total == ('T' << 1) + ('X' << 2) + ('T' << 3);

C'est tout à fait optimal, mais suppose que la somme des valeurs ASCII des autres extensions ne correspondra pas à la somme des valeurs ascii de l'extension .txt (j'ai également supposé que l'extension aura 3 caractères, comme vous l'avez fait ci-dessus):

int index = fName.length();
int total = fName[--index] + fName[--index] + fName[--index];
return total == 't' + 'x' + 't' || 'T' + 'X' + 'T';

Ceci est une version plus désordonnée de ce qui est ci-dessus, mais devrait être plus rapide:

return *((int*)&fName[index - 4]) == '.' + 't' + 'x' + 't';

Vous pouvez optimiser cela encore plus si vous savez qu'aucune des autres extensions ne se terminera par un "t", n'aura un "x" au milieu, etc. en faisant quelque chose comme ceci:

return fName[fName.length() - 1] == 't' || 'T;
-2
Cpp plus 1 20 juil. 2017 à 21:25

Que dis-tu de ça?

#include <string>
#include <algorithm>

template<typename CharT>
bool HasExtension(const std::basic_string<CharT>& fileName, const std::basic_string<CharT>& ext)
{
    auto b = fileName.begin() + fileName.length() - ext.length();
    auto a = ext.begin();

    while (b != fileName.end())
    {
        if (*a++ != tolower(*b++))
        {
             return false;
        }
    }
    return true;
}


int  main()
{
    std::string ext{".Txt"}; // make sure this is a lower case std::string.
    std::transform(ext.begin(), ext.end(), ext.begin(), tolower);  

    std::string fn{"test.txt"};

   return HasExtension(fn, ext) ? 0 : 1;
}
0
Michaël Roy 21 juil. 2017 à 15:21

Une solution suggérée serait

#include <iostream>
#include <string>

bool isTXT(const std::wstring& str)
{
    std::wstring::size_type idx;
    idx = str.rfind('.');
    if( idx != std::wstring::npos ){
        std::wstring ext = str.substr(idx+1);
        if( ext == L"txt" || ext == L"TXT" ) // do all possible combinations.
            return true;
    }
    return false;
}

int main()
{
    std::wstring fileName = L"haihs.TXT";
    std::wcout << isTXT(fileName) << std::endl;

    return 0;
}

Pour l'instruction conditionnelle ext == L"txt" || ext == L"TXT", vous pouvez remplir le reste si vous ne voulez pas créer une wstring pour la convertir en minuscules ou majuscules.

0
CroCo 22 juil. 2017 à 07:00