web-dev-qa-db-fra.com

Un moyen efficace de vérifier si std :: string n'a que des espaces

Je viens de parler avec un ami de ce qui serait le moyen le plus efficace de vérifier si un std :: string n'a que des espaces. Il doit le faire sur un projet intégré sur lequel il travaille et apparemment, ce type d'optimisation compte pour lui.

Je suis venu avec le code suivant, il utilise strtok().

bool has_only_spaces(std::string& str)
{
    char* token = strtok(const_cast<char*>(str.c_str()), " ");

    while (token != NULL)
    {   
        if (*token != ' ')
        {   
            return true;
        }   
    }   
    return false;
}

Je recherche des commentaires sur ce code et des moyens plus efficaces d'effectuer cette tâche sont également les bienvenus.

32
karlphillip
if(str.find_first_not_of(' ') != std::string::npos)
{
    // There's a non-space.
}
79
Mark B

En C++ 11, l'algorithme all_of peut être utilisé:

// Check if s consists only of whitespaces
bool whiteSpacesOnly = std::all_of(s.begin(),s.end(),isspace);
42
Michael Goldshteyn

Pourquoi tant de travail, autant de dactylographie?

bool has_only_spaces(const std::string& str) {
   return str.find_first_not_of (' ') == str.npos;
}
15
David Hammen

Ne serait-ce pas plus facile à faire:

bool has_only_spaces(const std::string &str)
{
    for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
    {
        if (*it != ' ') return false;
    }
    return true;
}

Cela présente l’avantage de revenir plus tôt dès qu’un caractère non espace est trouvé. Il sera donc légèrement plus efficace que les solutions qui examinent la chaîne entière.

6
Tom

Utiliser strtok comme ça, c'est du mauvais style! strtok modifies le tampon qu'il tokenise (remplace les caractères de délimitation par\0).

Voici une version non modifiable.

const char* p = str.c_str();
while(*p == ' ') ++p;
return *p != 0;

Il peut être optimisé encore davantage si vous le parcourez en morceaux de la machine Word. Pour être portable, vous devez également prendre en compte l'alignement. 

2

Pour vérifier si la chaîne n'a que des espaces dans c ++ 11:

bool is_whitespace(const std::string& s) {
  return std::all_of(s.begin(), s.end(), isspace);
}

en pré-c ++ 11:

bool is_whitespace(const std::string& s) {
  for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
    const char this_char = *it;
    if (!isspace(this_char)) {
      return false;
    }
  }
  return true;
}
2
ericcurtin

En voici un qui utilise uniquement STL (nécessite C++ 11)

inline bool isBlank(const std::string& s)
{
    return std::all_of(s.cbegin(),s.cend(),[](char c) { return std::isspace(c); });
}

Il se base sur le fait que si string est vide (begin = end) std :: all_of renvoie également true

Voici un petit programme de test: http://cpp.sh/2tx6

2
GameSalutes

il est très peu probable que vous battiez un algorithme naïf optimisé pour le compilateur, par exemple.

string::iterator it(str.begin()), end(str.end())    
for(; it != end && *it == ' '; ++it);
return it == end;

EDIT: En fait, il existe un moyen plus rapide (en fonction de la taille de la chaîne et de la mémoire disponible). 

std::string ns(str.size(), ' '); 
return ns == str;

EDIT: en fait, ce qui précède n’est pas rapide .. c’est stupide ... tenez-vous en à la mise en œuvre naïve, l’optimiseur sera partout ...

EDIT AGAIN: bon sang, je suppose que c'est mieux de regarder les fonctions dans std::string

return str.find_first_not_of(' ') == string::npos;
1
Nim

Je n'approuve pas vous const_casting ci-dessus et l'utilisation de strtok.

Un std :: string peut contenir des valeurs null incorporées, mais supposons qu'il s'agisse de 32 caractères ASCII avant d'appuyer sur le terminateur NULL.

Une façon de procéder consiste à utiliser une simple boucle et je supposerai que const char *.

bool all_spaces( const char * v )
{
   for ( ; *v; ++v )
   {
      if( *v != ' ' )
          return false;
   }
   return true;
}

Pour les chaînes plus volumineuses, vous pouvez consulter Word à la fois jusqu'au dernier mot, puis supposer que le mot 32 bits (par exemple) sera 0x20202020, ce qui peut être plus rapide.

1
CashCow

Quelque chose comme:

return std::find_if(
            str.begin(), str.end(),
            std::bind2nd( std::not_equal_to<char>(), ' ' ) )
    == str.end();

Si vous êtes intéressé par les espaces, et pas seulement le caractère d'espace, Alors la meilleure chose à faire est de définir un prédicat et de l'utiliser:

struct IsNotSpace
{
    bool operator()( char ch ) const
    {
        return ! ::is_space( static_cast<unsigned char>( ch ) );
    }
};

Si vous effectuez un traitement de texte, une collection de prédicats aussi simples Sera inestimable (et facile à générer automatiquement à partir de la liste des fonctions dans <ctype.h>).

1
James Kanze

Si vous utilisez CString, vous pouvez faire

CString myString = "    "; // All whitespace
if(myString.Trim().IsEmpty())
{
    // string is all whitespace
}

Cela présente l'avantage de supprimer tous les caractères de nouvelle ligne, espace et tabulation.

0
Steztric

Hm ... je ferais ça:

for (auto i = str.begin(); i != str.end() ++i)
    if (!isspace(i))
       return false;

Pseudo-code, isspace est situé dans cctype for C++.

Edit: Merci à James pour avoir signalé qu' isspace avait un comportement indéfini sur les caractères signés.

0
LainIwakura