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 nombre ou non (je saurai si c’est un paramètre de cette façon). J’ai écrit la fonction ci-dessous qui, selon moi, fonctionnait bien (ou j’ai accidentellement modifié pour l’arrêter ou je suis schizophrène ou Windows est schizophrène):

bool isParam(ssortingng line){ if(isdigit(atoi(line.c_str()))) return true; return false; } 

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

 bool is_number(const std::ssortingng& s) { std::ssortingng::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 de la manière C ++ 11:

 bool is_number(const std::ssortingng& 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 avez besoin de détecter des nombres entiers ou des fractions négatives, vous devriez opter pour une solution plus robuste basée sur une bibliothèque. Bien que l’ajout de la prise en charge des entiers négatifs soit assez sortingvial.

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

 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 manipuler des fractions ou une notation scientifique, strtod plutôt strtod (vous obtiendrez un résultat double ).

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

Votre fonction peut alors être écrite comme

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

Vous pouvez le faire de 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(str); // double could be anything with >> operator. } catch(...) { oops, not a number } 

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 num en entier, vous obtenez une erreur si la chaîne contient un point.

 #include #include using namespace std; int main() { ssortingng s; cin >> s; ssortingngstream 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 ++

Je voulais juste lancer cette idée qui utilise l’itération mais un autre code fait cette itération:

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

Ce n’est pas robuste comme cela devrait être lors de la vérification d’un point décimal ou d’un signe moins car cela permet d’avoir plus d’un pour chacun et à n’importe quel endroit. La bonne chose est que c’est une seule ligne de code et ne nécessite pas de bibliothèque tierce.

Sortez le ‘.’ et ‘-‘ si des entiers positifs sont tout ce qui est autorisé.

Avec le compilateur C ++ 11, pour les entiers non négatifs, j’utiliserais quelque chose comme ceci (notez le :: au lieu de std:: :

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

http://ideone.com/OjVJWh

Je suggère une approche de regex. Un regex-match complet (par exemple, en utilisant boost :: regex ) avec

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

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

Autres variations:

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

(seulement positif)

 -?[0-9]+ 

(uniquement entier)

 [0-9]+ 

(uniquement entier positif)

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

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

Essaye ça:

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

Voici une solution pour vérifier les entiers positifs:

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

Brendan ça

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

est presque ok

en supposant que n’importe quelle chaîne commençant par 0 est un nombre, ajoutez simplement une vérification pour ce cas

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

ofc “123hello” sera vrai comme Tony D l’a noté.

J’ai trouvé le code suivant le plus robuste (c ++ 11). Il intercepte les entiers et les flottants.

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

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

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

Comme avec la réponse de David Rector, il n’est pas robuste aux chaînes comportant plusieurs signes ou signes moins, mais vous pouvez supprimer ces caractères pour vérifier les entiers.


Cependant, je suis partiel avec une solution, basée sur la solution de Ben Voigt , en utilisant strtod dans cstdlib pour examiner les valeurs décimales, la notation scientifique / technique, la notation hexadécimale (C ++ 11) ou même INF / INFINITY / NAN (C ++ 11 ) est:

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

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

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

donc vous pouvez essayer la fonction suivante qui peut fonctionner avec les deux (Unicode et ANSI)

 bool IsNumber(CSsortingng Cs){ Cs.Trim(); #ifdef _UNICODE std::wssortingng sr = (LPCWSTR)Cs.GetBuffer(Cs.GetLength()); return std::regex_match(sr, std::wregex(_T("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"))); #else std::ssortingng s = (LPCSTR)Cs.GetBuffer(); return std::regex_match(s, std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?")); #endif } 
 include  

Pour valider les doublons:

 bool validateDouble(const std::ssortingng & input) { int decimals = std::count(input.begin(), input.end(), '.'); // The number of decimals in the ssortingng int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the ssortingng 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 les ints (avec les négatifs)

 bool validateInt(const std::ssortingng & input) { int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the ssortingng 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 les ints non signés

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

}

 bool isNumeric(ssortingng s){ if ( !s.empty() && s[0] != '-' ) s = "0" + s; //prepend 0 ssortingng garbage; ssortingngstream ss(s); ss >> *(auto_ptr(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: la chaîne de caractères >> surcharge peut convertir des chaînes en différents types arithmétiques en lisant les caractères séquentiellement à partir de la chaîne de caractères (ss dans ce cas) jusqu’à ce qu’ils soient à court de caractères OU le caractère suivant ne répond pas aux critères dans le type de variable de destination.

Exemple 1:

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

exemple2:

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

exemple3:

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

l’explication variable “garbage” “:

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

notice 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 faire 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 après l’extraction initiale dans la variable de type double. Si quelque chose rest, il sera lu dans “garbage”, ce qui signifie que la chaîne complète passée n’est pas un nombre (il commence juste par un). dans ce cas, nous voudrions retourner faux;

le “0” explication “

Si vous tentez d’extraire un seul caractère en double, vous échouerez (en renvoyant 0 dans notre double), mais vous déplacerez toujours la position du tampon de chaîne après le caractère. Dans ce cas, notre lecture de la mémoire sera vide, ce qui entraînerait un retour incorrect de la fonction. pour contourner ce problème, j’ai ajouté un 0 à la chaîne pour que, par exemple, si la chaîne transmise était “a”, elle soit remplacée par “0a” pour que le 0 soit extrait dans le double et “a” extrait.

Si vous ajoutez un 0, cela n’aura aucune incidence sur la valeur du nombre. Le nombre sera donc correctement extrait dans notre double variable.

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

  #include  bool isNumber(ssortingng str) { double d; issortingngstream is(str); is >> d; return !is.fail() && is.eof(); } 

Le plus simple que je puisse penser en c ++

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

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

Ma solution utilisant C ++ 11 regex ( #include ), il peut être utilisé pour une vérification plus précise, 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::ssortingng& str_) { return std::regex_match(str_, INT_TYPE); } bool isUnsignedIntegerType(const std::ssortingng& str_) { return std::regex_match(str_, UNSIGNED_INT_TYPE); } bool isDoubleType(const std::ssortingng& str_) { return std::regex_match(str_, DOUBLE_TYPE); } bool isUnsignedDoubleType(const std::ssortingng& str_) { return std::regex_match(str_, UNSIGNED_DOUBLE_TYPE); } 

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

Comme il m’a été révélé dans une réponse à ma question, je pense que vous devriez utiliser boost :: conversion :: try_lexical_convert

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

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

Il y a quelques mois, j’ai implémenté un moyen de déterminer si une chaîne est un nombre 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,

 ssortingng val; // the ssortingng 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 réaliser que la fonction retournera un 0 si le nombre n'a pas été détecté. Le 0 il peut être traité comme faux (comme booléen).

Je propose une convention simple:

Si la conversion en ASCII est> 0 ou si elle commence par 0, alors c’est un nombre. Ce n’est pas parfait mais rapide.

Quelque chose comme ça:

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

Encore une autre réponse, qui utilise stold (même si vous pouvez également utiliser stof / stod si vous n’avez pas besoin de la précision).

 bool isNumeric(const std::ssortingng& ssortingng) { std::size_t pos; long double value = 0.0; try { value = std::stold(ssortingng, &pos); } catch(std::invalid_argument&) { return false; } catch(std::out_of_range&) { return false; } return pos == ssortingng.size() && !std::isnan(value); } 

Nous pouvons utiliser une classe ssortingngstream .

  bool isNumeric(ssortingng str) { ssortingngstream stream; double number; stream<>number; return stream.eof(); } 

En utilisant . Ce code a été testé!

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

Cette fonction prend en charge tous les cas possibles:

 bool AppUtilities::checkSsortingngIsNumber(std::ssortingng s){ //Eliminate obvious irritants that could spoil the party //Handle special cases here, eg 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::ssortingng::npos) s.replace(start_pos, 1, ""); //If the ssortingng 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::ssortingng::const_iterator it = s.begin(); while (it != s.end() && std::isdigit(*it)) ++it; return !s.empty() && it == s.end(); //Tada.... }