web-dev-qa-db-fra.com

Comment vérifier si une chaîne C ++ est un int?

Lorsque j'utilise getline, je saisis un tas de chaînes ou de nombres, mais je veux que la boucle while génère le "Word" si ce n'est pas un nombre. Existe-t-il un moyen de vérifier si "Word" est un nombre ou non? Je sais que je pourrais utiliser atoi() pour les chaînes C mais qu'en est-il des chaînes de la classe de chaînes?

int main () {
  stringstream ss (stringstream::in | stringstream::out);
  string Word;
  string str;
  getline(cin,str);
  ss<<str;
  while(ss>>Word)
    {
      //if(    )
        cout<<Word<<endl;
    }
}
37
user342231

Une autre version...

Utilisez strtol , en l'enveloppant dans une fonction simple pour masquer sa complexité:

inline bool isInteger(const std::string & s)
{
   if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false;

   char * p;
   strtol(s.c_str(), &p, 10);

   return (*p == 0);
}

Pourquoi strtol?

Pour autant que j'aime C++, parfois l'API C est la meilleure réponse en ce qui me concerne:

  • l'utilisation d'exceptions est exagérée pour un test autorisé à échouer
  • la création temporaire d'objet de flux par la distribution lexicale est exagérée et trop inefficace lorsque la bibliothèque standard C a une fonction dédiée peu connue qui fait le travail.

Comment ça marche ?

strtol semble assez brut à première vue, donc une explication rendra le code plus simple à lire:

strtol analysera la chaîne, s'arrêtant au premier caractère qui ne peut pas être considéré comme faisant partie d'un entier. Si vous fournissez p (comme je l'ai fait ci-dessus), il définit p juste à ce premier caractère non entier.

Mon raisonnement est que si p n'est pas défini à la fin de la chaîne (le caractère 0), alors il y a un caractère non entier dans la chaîne s, ce qui signifie s n'est pas un entier correct.

Les premiers tests sont là pour éliminer les cas d'angle (espaces de tête, chaîne vide, etc.).

Cette fonction doit bien sûr être adaptée à vos besoins (les espaces de début sont-ils une erreur? Etc.).

Sources :

Voir la description de strtol sur: http://en.cppreference.com/w/cpp/string/byte/strtol .

Voir aussi la description des fonctions sœurs de strtol (strtod, strtoul, etc.).

66
paercebal

La réponse acceptée donnera un faux positif si l'entrée est un nombre plus un texte, car "stol" convertira les premiers chiffres et ignorera le reste.

J'aime le plus la version suivante, car c'est un Nice one-liner qui n'a pas besoin de définir une fonction et vous pouvez simplement copier et coller où vous en avez besoin.

#include <string>

...

std::string s;

bool has_only_digits = (s.find_first_not_of( "0123456789" ) == std::string::npos);

EDIT: si vous aimez cette implémentation mais que vous voulez l'utiliser en tant que fonction, alors cela devrait faire:

bool has_only_digits(const string s){
  return s.find_first_not_of( "0123456789" ) == string::npos;
}
56
Ferdinando Randisi

Vous pourriez essayer boost::lexical_cast. Il jette un bad_lexical_cast exception en cas d'échec.

Dans ton cas:

int number;
try
{
  number = boost::lexical_cast<int>(Word);
}
catch(boost::bad_lexical_cast& e)
{
  std::cout << Word << "isn't a number" << std::endl;
}
16
Karl von Moor

Si vous vérifiez simplement si Word est un nombre, ce n'est pas trop difficile:

#include <ctype.h>

...

string Word;
bool isNumber = true;
for(string::const_iterator k = Word.begin(); k != Word.end(); ++k)
    isNumber &&= isdigit(*k);

Optimisez comme vous le souhaitez.

8
Mike DeSimone

Utilisez les fonctions C stdio/string toutes puissantes:

int dummy_int;
int scan_value = std::sscanf( some_string.c_str(), "%d", &dummy_int);

if (scan_value == 0)
    // does not start with integer
else
    // starts with integer
4
cmo

Vous pouvez utiliser boost::lexical_cast, comme suggéré, mais si vous avez une connaissance préalable des chaînes (c'est-à-dire que si une chaîne contient un littéral entier, elle n'aura pas d'espace de tête, ou que les entiers ne sont jamais écrits avec des exposants), puis rouler votre propre fonction devrait être à la fois plus efficace et pas particulièrement difficile.

4
James

Ok, la façon dont je le vois, vous avez 3 options.

1: Si vous souhaitez simplement vérifier si le nombre est un entier, et ne vous souciez pas de le convertir, mais souhaitez simplement le conserver sous forme de chaîne et ne vous souciez pas des débordements potentiels, vérifier s'il correspond à une expression régulière pour un entier serait idéal ici.

2: Vous pouvez utiliser boost :: lexical_cast puis intercepter une exception boost :: bad_lexical_cast potentielle pour voir si la conversion a échoué. Cela fonctionnerait bien si vous pouvez utiliser le boost et si l'échec de la conversion est une condition exceptionnelle.

3: Lancez votre propre fonction similaire à lexical_cast qui vérifie la conversion et retourne vrai/faux selon qu'elle réussit ou non. Cela fonctionnerait dans le cas où 1 & 2 ne correspond pas à vos besoins.

3
Jacob

J'ai modifié méthode de paercebal pour répondre à mes besoins:

typedef std::string String;    

bool isInt(const String& s, int base){
   if(s.empty() || std::isspace(s[0])) return false ;
   char * p ;
   strtol(s.c_str(), &p, base) ;
   return (*p == 0) ;
}


bool isPositiveInt(const String& s, int base){
   if(s.empty() || std::isspace(s[0]) || s[0]=='-') return false ;
   char * p ;
   strtol(s.c_str(), &p, base) ;
   return (*p == 0) ;
}


bool isNegativeInt(const String& s, int base){
   if(s.empty() || std::isspace(s[0]) || s[0]!='-') return false ;
   char * p ;
   strtol(s.c_str(), &p, base) ;
   return (*p == 0) ;
}

Remarque:

  1. Vous pouvez vérifier différentes bases (binaire, oct, hex et autres)
  2. Assurez-vous que vous ne réussissez pas 1, valeur négative ou valeur >36 comme base.
  3. Si vous réussissez 0 comme base, il détectera automatiquement la base, c'est-à-dire pour une chaîne commençant par 0x sera traité comme un hex et une chaîne commençant par 0 sera traité comme oct. Les caractères ne respectent pas la casse.
  4. Tout espace blanc dans la chaîne le fera retourner faux.
1
Jahid

Voici une autre solution.

try
{
  (void) std::stoi(myString); //cast to void to ignore the return value   
  //Success! myString contained an integer
} 
catch (const std::logic_error &e)
{   
  //Failure! myString did not contain an integer
}
1
Benjamin
template <typename T>
const T to(const string& sval)
{
        T val;
        stringstream ss;
        ss << sval;
        ss >> val;
        if(ss.fail())
                throw runtime_error((string)typeid(T).name() + " type wanted: " + sval);
        return val;
}

Et puis vous pouvez l'utiliser comme ça:

double d = to<double>("4.3");

ou

int i = to<int>("4123");
1
Nicolas Viennot