web-dev-qa-db-fra.com

Comment déterminer si une chaîne est un nombre avec C++?

J'ai eu pas mal de mal à écrire une fonction qui vérifie si une chaîne est un nombre. Pour un jeu que j'écris, je dois juste vérifier si une ligne du fichier que je lis est un numéro ou non (je saurai s'il s'agit d'un paramètre de cette façon). J'ai écrit la fonction ci-dessous qui, je crois, fonctionnait sans problème (ou j'ai modifié par inadvertance pour l'arrêter ou je suis schizophrénique ou Windows est schizophrénique):

bool isParam (string line)
{
    if (isdigit(atoi(line.c_str())))
        return true;

    return false;
}
98
Brendan Weinstein

Le moyen le plus efficace serait simplement de parcourir la chaîne jusqu'à ce que vous trouviez un caractère non numérique. S'il existe des caractères non numériques, vous pouvez considérer la chaîne et non un nombre.

bool is_number(const std::string& s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && std::isdigit(*it)) ++it;
    return !s.empty() && it == s.end();
}

Ou si vous voulez le faire à la manière C++ 11:

bool is_number(const std::string& s)
{
    return !s.empty() && std::find_if(s.begin(), 
        s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
}

Comme indiqué dans les commentaires ci-dessous, cela ne fonctionne que pour les entiers positifs. Si vous devez détecter des entiers ou des fractions négatifs, choisissez une solution plus robuste basée sur une bibliothèque. Bien que, ajouter le support pour les entiers négatifs est assez trivial.

110
Charles Salvia

Pourquoi réinventer la roue? La bibliothèque standard C (également disponible en C++) a une fonction qui remplit exactement cette fonction:

char* p;
long converted = strtol(s, &p, 10);
if (*p) {
    // conversion failed because the input wasn't a number
}
else {
    // use converted
}

Si vous voulez gérer les fractions ou la notation scientifique, choisissez plutôt strtod (vous obtiendrez un résultat double).

Si vous souhaitez autoriser les constantes hexadécimales et octales dans le style C/C++ ("0xABC"), définissez le dernier paramètre 0 à la place.

Votre fonction peut alors être écrite comme

bool isParam(string line)
{
    char* p;
    strtol(line.c_str(), &p, 10);
    return *p == 0;
}
70
Ben Voigt

Vous pouvez le faire à la manière C++ avec boost :: lexical_cast. Si vous insistez vraiment pour ne pas utiliser de boost, vous pouvez simplement examiner ce qu'il fait et le faire. C'est assez simple.

try 
{
  double x = boost::lexical_cast<double>(str); // double could be anything with >> operator.
}
catch(...) { oops, not a number }
27
Crazy Eddie

Avec le compilateur C++ 11, pour les entiers non négatifs, je voudrais utiliser quelque chose comme ceci (notez le :: au lieu de std::):

bool is_number(const std::string &s) {
  return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
}

http://ideone.com/OjVJWh

16
szx

Je voulais juste ajouter cette idée qui utilise l'itération mais un autre code l'itère:

#include <string.h>

bool is_number(const std::string& s)
{
    return( strspn( s.c_str(), "-.0123456789" ) == s.size() );
}

Ce n’est pas aussi robuste que cela devrait être lorsqu’il vérifie la présence d’un signe décimal ou d’un signe moins, étant donné qu’il en existe plusieurs. La bonne chose est que c'est une seule ligne de code et ne nécessite pas une bibliothèque tierce.

Sortez le '.' et '-' si les entiers positifs sont tous autorisés.

13
David Rector

Avec cette solution, vous pouvez tout vérifier, des nombres négatifs aux nombres positifs et même des nombres flottants. Lorsque vous modifiez le type de num en entier, vous obtenez une erreur si la chaîne contient un point.

#include<iostream>
#include<sstream>
using namespace std;


int main()
{
      string s;

      cin >> s;

      stringstream ss;
      ss << s;

      float num = 0;

      ss >> num;

      if(ss.good()) {
          cerr << "No Valid Number" << endl;
      }
      else if(num == 0 && s[0] != '0') {
          cerr << "No Valid Number" << endl;
      }
      else {
          cout << num<< endl;
      }             
}

Prouver: Programme C++

12
tom1991te

Je suggérerais une approche regex. Une correspondance regex complète (par exemple, en utilisant boost :: regex ) avec 

-?[0-9]+([.][0-9]+)?

montrerait si la chaîne est un nombre ou non. Cela inclut les nombres positifs et négatifs, les nombres entiers et décimaux.

Autres variations:

[0-9]+([.][0-9]+)?

(seulement positif)

-?[0-9]+

(seulement entier)

[0-9]+

(seul entier positif)

11
Mephane

Voici une autre façon de le faire en utilisant la bibliothèque <regex>:

bool is_integer(const std::string & s){
    return std::regex_match(s, std::regex("[(-|+)|][0-9]+"));
}
7
mpataki14

J'ai trouvé le code suivant le plus robuste (c ++ 11). Il capture à la fois les entiers et les flottants. 

bool isNumber( std::string token )
{
    using namespace std;
    return std::regex_match( token, std::regex( ( "((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?" ) ) );
}
4
dk123

Essaye ça:

isNumber(const std::string &str) {    
  return !str.empty() && str.find_first_not_of("0123456789") == string::npos;
}
3
Tomasz

Brendan ce

bool isNumber(string line) 
{
    return (atoi(line.c_str())); 
}

est presque ok.

en supposant que toute chaîne commençant par 0 soit un nombre, .__, il suffit d'ajouter une coche pour ce cas

bool isNumber(const string &line) 
{
 if (line[0] == '0') return true;
 return (atoi(line.c_str()));
}

ofc "123hello" reviendra vrai comme l'a noté Tony D.

2
Noam Geffen

Voici une solution pour vérifier les entiers positifs:

bool isPositiveInteger(const std::string& s)
{
    return !s.empty() && 
           (std::count_if(s.begin(), s.end(), std::isdigit) == s.size());
}
2
Jaime Soto

Le plus simple que je puisse penser en c ++

bool isNumber(string s) {
    if(s.size()==0) return false;
    for(int i=0;i<s.size();i++) {
        if((s[i]>='0' && s[i]<='9')==false) {
            return false;
        }
    }
    return true;
}

Exemple de code de travail: https://ideone.com/nRX51Y

2
Abhijit Annaldas
bool isNumeric(string s){
    if ( !s.empty() && s[0] != '-' )
        s = "0" + s; //prepend 0

    string garbage;

    stringstream ss(s); 
    ss >> *(auto_ptr<double>(new double)) >> garbage;
/*
//the line above extracts the number into an anonymous variable. it could also be done like this:
double x;
ss >> x >> garbage;
*/
    //if there is no garbage return true or else return false
    return garbage.empty(); 
}

comment ça marche: le stringstream >> overload peut convertir des chaînes en différents types arithmétiquesit le fait en lisant des caractères de manière séquentielle dans le stringstream (ss dans ce cas) jusqu’à épuisement des caractères OR le caractère suivant ne répond pas aux critères pour être stocké dans le type de variable de destination.

exemple 1:

stringstream ss("11");
double my_number;
ss >> my_number; //my number = 11

exemple2:

stringstream ss("011");
double my_number;
ss >> my_number; //my number = 11

exemple3:

stringstream ss("11ABCD");
double my_number;
ss >> my_number; //my number = 11 (even though there are letters after the 11)

l'explication de la variable "garbage" ":

pourquoi ne pas vérifier si l'extraction dans mon double a une valeur valide, puis renvoyer true si c'est le cas?

remarque example3 ci-dessus lira toujours avec succès le nombre 11 dans la variable my_number même si la chaîne d'entrée est "11ABCD" (qui n'est pas un nombre).

pour gérer ce cas, nous pouvons effectuer une autre extraction dans une variable de chaîne (que j'ai nommée garbage) qui peut lire tout ce qui peut avoir été laissé dans le tampon de chaîne après l'extraction initiale dans la variable de type double. S'il reste quelque chose, il sera lu dans "poubelle", ce qui signifie que la chaîne complète transmise n'est pas un nombre (elle commence simplement par un). dans ce cas, nous voudrions renvoyer faux;

l'explication "0" préparée ":

tenter d'extraire un seul caractère dans un double échouera (en renvoyant 0 dans notre double) mais déplacera toujours la position du tampon de chaîne après le caractère. Dans ce cas, notre lecture de poubelle sera vide, ce qui entraînerait un retour incorrect de la fonction par true . Pour éviter ce problème, j'ai ajouté un 0 à la chaîne. Ainsi, si, par exemple, la chaîne transmise était "a", elle est remplacée par "0a" pour que le 0 soit extrait dans le double et "a" soit extrait dans la poubelle. 

le préfixe 0 n'affectera pas la valeur du nombre, il sera donc correctement extrait dans notre double variable.

1
KorreyD

Comme cela m’a été révélé dans un answer à ma question, j'estime que vous devriez utiliser boost :: conversion :: try_lexical_convert

1
NoSenseEtAl

Une solution basée sur un commentaire de kbjorklu est:

bool isNumber(const std::string& s)
{
   return !s.empty() && s.find_first_not_of("-.0123456789") == std::string::npos;
}

Comme avec La réponse de David Rector elle n'est pas robuste pour les chaînes avec plusieurs points ou signes moins, mais vous pouvez supprimer ces caractères pour vérifier uniquement les entiers.


Cependant, je suis partisan d’une solution basée sur la solution de Ben Voigt , utilisant strtod dans cstdlib pour rechercher des valeurs décimales, une notation scientifique/technique, une notation hexidécimale (C++ 11), voire INF/INFINITY/NAN C++ 11) est:

bool isNumberC(const std::string& s)
{
    char* p;
    strtod(s.c_str(), &p);
    return *p == 0;
}
1
chappjc

pour vérifier si une chaîne est un nombre entier ou un nombre à virgule flottante, vous pouvez utiliser:

 #include <sstream>

    bool isNumber(string str) {
    double d;
    istringstream is(str);
    is >> d;
    return !is.fail() && is.eof();
}
1
MuhammadKhalifa

Je pense que cette expression régulière devrait traiter presque tous les cas

"^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"

afin que vous puissiez essayer la fonction suivante qui peut fonctionner avec les deux (Unicode et ANSI)

bool IsNumber(CString Cs){
Cs.Trim();

#ifdef _UNICODE
std::wstring sr = (LPCWSTR)Cs.GetBuffer(Cs.GetLength());
return std::regex_match(sr, std::wregex(_T("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?")));

#else
    std::string s = (LPCSTR)Cs.GetBuffer();
return std::regex_match(s, std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
#endif
}
1
Motaz
include <string>

Pour valider les doubles:

bool validateDouble(const std::string & input) {
int decimals = std::count(input.begin(), input.end(), '.'); // The number of decimals in the string
int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the string

if (input.size() == decimals + negativeSigns) // Consists of only decimals and negatives or is empty
    return false;
else if (1 < decimals || 1 < negativeSigns) // More than 1 decimal or negative sign
    return false;
else if (1 == negativeSigns && input[0] != '-') // The negative sign (if there is one) is not the first character
    return false;
else if (strspn(input.c_str(), "-.0123456789") != input.size()) // The string contains a character that isn't in "-.0123456789"
    return false;
return true;

}

Pour valider Ints (avec négatifs)

bool validateInt(const std::string & input) {
int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the string

if (input.size() == negativeSigns) // Consists of only negatives or is empty
    return false;
else if (1 < negativeSigns) // More than 1 negative sign
    return false;
else if (1 == negativeSigns && input[0] != '-') // The negative sign (if there is one) is not the first character
    return false;
else if (strspn(input.c_str(), "-0123456789") != input.size()) // The string contains a character that isn't in "-0123456789"
    return false;
return true;

}

Pour valider des entiers non signés

bool validateUnsignedInt(const std::string & input) {
return (input.size() != 0 && strspn(input.c_str(), "0123456789") == input.size()); // The string is not empty and contains characters only in "0123456789"

}

1
Erik Nordin

Après avoir un peu plus consulté la documentation, j'ai trouvé une réponse qui répond à mes besoins mais ne sera probablement pas aussi utile pour les autres. La voici (sans le retour ennuyeux true et les fausses déclarations :-))

bool isNumber(string line) 
{
    return (atoi(line.c_str())); 
}
0
Brendan Weinstein

Nous pouvons utiliser un stringstream class. 

    bool isNumeric(string str)
    {
       stringstream stream;                   
       double number;

       stream<<str;
       stream>>number;

       return stream.eof();
    }
0
rashedcs

Ma solution utilisant C++ 11 regex (#include <regex>) peut être utilisée pour un contrôle plus précis, comme unsigned int, double etc.

static const std::regex INT_TYPE("[+-]?[0-9]+");
static const std::regex UNSIGNED_INT_TYPE("[+]?[0-9]+");
static const std::regex DOUBLE_TYPE("[+-]?[0-9]+[.]?[0-9]+");
static const std::regex UNSIGNED_DOUBLE_TYPE("[+]?[0-9]+[.]?[0-9]+");

bool isIntegerType(const std::string& str_)
{
  return std::regex_match(str_, INT_TYPE);
}

bool isUnsignedIntegerType(const std::string& str_)
{
  return std::regex_match(str_, UNSIGNED_INT_TYPE);
}

bool isDoubleType(const std::string& str_)
{
  return std::regex_match(str_, DOUBLE_TYPE);
}

bool isUnsignedDoubleType(const std::string& str_)
{
  return std::regex_match(str_, UNSIGNED_DOUBLE_TYPE);
}

Vous pouvez trouver ce code sur http://ideone.com/lyDtfi , il peut être facilement modifié pour répondre aux exigences.

0
aniliitb10

Cette fonction prend en charge tous les cas possibles:

bool AppUtilities::checkStringIsNumber(std::string s){
    //Eliminate obvious irritants that could spoil the party
    //Handle special cases here, e.g. return true for "+", "-", "" if they are acceptable as numbers to you
    if (s == "" || s == "." || s == "+" || s == "-" || s == "+." || s == "-.") return false;

    //Remove leading / trailing spaces **IF** they are acceptable to you
    while (s.size() > 0 && s[0] == ' ') s = s.substr(1, s.size() - 1);
    while (s.size() > 0 && s[s.size() - 1] == ' ') s = s.substr(0, s.size() - 1);


    //Remove any leading + or - sign
    if (s[0] == '+' || s[0] == '-')
        s = s.substr(1, s.size() - 1);

    //Remove decimal points
    long prevLength = s.size();

    size_t start_pos = 0;
    while((start_pos = s.find(".", start_pos)) != std::string::npos) 
        s.replace(start_pos, 1, "");

    //If the string had more than 2 decimal points, return false.
    if (prevLength > s.size() + 1) return false;

    //Check that you are left with numbers only!!
    //Courtesy selected answer by Charles Salvia above
    std::string::const_iterator it = s.begin();
    while (it != s.end() && std::isdigit(*it)) ++it;
    return !s.empty() && it == s.end();

    //Tada....
}
0
Vaibhav Gupta

Il y a quelques mois, j'ai mis en place un moyen de déterminer si une chaîne est un entier, hexadécimal ou double.

enum{
        STRING_IS_INVALID_NUMBER=0,
        STRING_IS_HEXA,
        STRING_IS_INT,
        STRING_IS_DOUBLE
};

bool isDigit(char c){
    return (('0' <= c) && (c<='9'));
}

bool isHexaDigit(char c){
    return ((('0' <= c) && (c<='9')) || ((tolower(c)<='a')&&(tolower(c)<='f')));
}


char *ADVANCE_DIGITS(char *aux_p){

    while(CString::isDigit(*aux_p)) aux_p++;
    return aux_p;
}

char *ADVANCE_HEXADIGITS(char *aux_p){

    while(CString::isHexaDigit(*aux_p)) aux_p++;
    return aux_p;
}


int isNumber(const string & test_str_number){
    bool isHexa=false;
    char *str = (char *)test_str_number.c_str();

    switch(*str){
    case '-': str++; // is negative number ...
               break;
    case '0': 
              if(tolower(*str+1)=='x')  {
                  isHexa = true;
                  str+=2;
              }
              break;
    default:
            break;
    };

    char *start_str = str; // saves start position...
    if(isHexa) { // candidate to hexa ...
        str = ADVANCE_HEXADIGITS(str);
        if(str == start_str)
            return STRING_IS_INVALID_NUMBER;

        if(*str == ' ' || *str == 0) 
            return STRING_IS_HEXA;

    }else{ // test if integer or float
        str = ADVANCE_DIGITS(str);
        if(*str=='.') { // is candidate to double
            str++;
            str = ADVANCE_DIGITS(str);
            if(*str == ' ' || *str == 0)
                return STRING_IS_DOUBLE;

            return STRING_IS_INVALID_NUMBER;
        }

        if(*str == ' ' || *str == 0)
            return STRING_IS_INT;

    }

    return STRING_IS_INVALID_NUMBER;


}

Ensuite, dans votre programme, vous pouvez facilement convertir le nombre en fonction de son type si vous procédez comme suit:

string val; // the string to check if number...

switch(isNumber(val)){
   case STRING_IS_HEXA: 
   // use strtol(val.c_str(), NULL, 16); to convert it into conventional hexadecimal
   break;
   case STRING_IS_INT: 
   // use (int)strtol(val.c_str(), NULL, 10); to convert it into conventional integer
   break;
   case STRING_IS_DOUBLE:
   // use atof(val.c_str()); to convert it into conventional float/double
   break;
}

Vous pouvez vous rendre compte que la fonction retournera 0 si le nombre n'a pas été détecté. Le 0, il peut être traité comme faux (comme booléen).

0
Jordi Espada

Style C/C++ pour les entiers non signés, à l'aide de la plage for C++ 11:

int isdigits(const std::string & s)
{
    for (char c : s) if (!isdigit(c)) return (0);
    return (1);
}
0
Kubator

Je propose une convention simple:

Si la conversion en ASCII est> 0 ou si elle commence par 0, il s'agit d'un nombre. Ce n'est pas parfait mais rapide.

Quelque chose comme ça:

string token0;

if (atoi(token0.c_str())>0 || isdigit(token0.c_str()[0]) ) { //this is a value
    // do what you need to do...
}
0
Gokhan Unel

Utiliser <regex>. Ce code a été testé!

bool isNumber(const std::string &token)
{
    return std::regex_match(token, std::regex("(\\+|-)?[0-9]*(\\.?([0-9]+))$"));
}
0
Nery Jr

Essaye ça:

bool checkDigit(string str)
{  
   int n=str.length();

   for(int i=0;    i   < n ;   i++)
   {
     if(str[i]<'0' || str[i]>'9')
       return false;
   }

   return true;
}
0
rrlinus

Encore une autre réponse, qui utilise stold (bien que vous puissiez aussi utiliser stof/stod si vous n’avez pas besoin de la précision).

bool isNumeric(const std::string& string)
{
    std::size_t pos;
    long double value = 0.0;

    try
    {
        value = std::stold(string, &pos);
    }
    catch(std::invalid_argument&)
    {
        return false;
    }
    catch(std::out_of_range&)
    {
        return false;
    }

    return pos == string.size() && !std::isnan(value);
}
0
Tim Angus

Pourriez-vous simplement utiliser le code de retour de sscanf pour déterminer s'il s'agit d'un int?

bool is_number(const std::string& s)
{
    int value;
    int result = sscanf(valueStr.c_str(), "%d", &value);
    return (result != EOF && readResult != 0);
}
0
David D