2016-06-22 1 views
1

J'ai entendu dire que je devrais utiliser strtol au lieu de atoi en raison de sa meilleure gestion des erreurs. Je voulais voir si je pouvais utiliser ce code pour vérifier si une chaîne est un entier:C++: vérifier si chaîne est un entier valide en utilisant "strtol"

#include <iostream> 
#include <stdlib.h> 

using namespace std; 

int main() 
{ 
    string testString = "ANYTHING"; 
    cout << "testString = " << testString << endl; 
    int testInt = strtol(testString.c_str(),NULL,0); 
    cout << "errno = " << errno << endl; 
    if (errno > 0) 
    { 
     cout << "There was an error." << endl; 
     cout << "testInt = " << testInt << endl; 
    } 
    else 
    { 
     cout << "Success." << endl; 
     cout << "testInt = " << testInt << endl; 
    } 
    return 0; 
} 

Je l'ai remplacé ANYTHING avec 5 et cela a fonctionné parfaitement:

testString = 5 
errno = 0 
Success. 
testInt = 5 

Et quand je le fais avec 2147483648 , le plus grand possible int + 1, elle renvoie ceci:

testString = 2147483648 
errno = 34 
There was an error. 
testInt = 2147483647 

assez juste. Mais, quand je l'essaye avec Hello world!, ce arrive:

testString = Hello world! 
errno = 0 
Success. 
testInt = 0 

Qu'est-ce que je voulais faire ici? Et s'il vous plaît ne me donnez pas quelque chose de compliqué pour quelque chose d'aussi simple que de valider qu'une chaîne est un int.

utilisant: GNU compilateur GCC, Code :: Blocks, Windows
"Avez-g ++ suivre le 11 C++ standard langue ISO C++ [std = c 11 ++]" a été cochée dans "Drapeaux de compilation".

+1

passe argument approprié au lieu de 'null' à [strtol] (http: //www.cplusplus.com/reference/cstdlib/strtol /) pour connaître la plage valide. – Jarod42

+0

[Aucun repro] (http://coliru.stacked-crooked.com/a/021660de72cf66f4) pour votre 2ème cas de test. –

+0

@ πάνταῥεῖ, Cela dépend si 'long' peut contenir ce nombre. Un «long» de 32 bits ne peut pas. – chris

Répondre

2

Selon des the man page of strtol. Vous devez définir votre fonction telle que:

bool isNumeric(const std::string& str) { 
    char *end; 
    long val = std::strtol(str.c_str(), &end, 10); 
    if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) { 
     // if the converted value would fall out of the range of the result type. 
     return false; 
    } 
    if (end == str) { 
     // No digits were found. 
     return false; 
    } 
    // check if the string was fully processed. 
    return *end == '\0'; 
} 

En C++ 11, je préfère utiliser std::stol au lieu de std::strtol, tels que:

bool isNumeric(const std::string& str) { 
    try { 
     size_t sz; 
     std::stol(str, &sz); 
     return sz == str.size(); 
    } catch (const std::invalid_argument&) { 
     // if no conversion could be performed. 
     return false; 
    } catch (const std::out_of_range&) { 
     // if the converted value would fall out of the range of the result type. 
     return false; 
    } 
} 

std::stol appels std::strtol, mais vous travaille directement avec std::string et le code est simplifié.

+0

j'ai entendu que 'try' devrait être évité si possible – FluorescentGreen5

+1

@ FluorescentGreen5, la meilleure pratique actuelle sur try/catch est que le code lisible robuste devrait être préféré par-dessus tout donc parfois try/catch sera l'option. De plus, certaines des nouvelles fonctionnalités de la bibliothèque C++ 11 requièrent try/catch pour obtenir la fonctionnalité désirée comme dans l'exemple ci-dessus avec 'std :: stol'. – keith

+0

Votre premier extrait de code a fait le travail, cependant, je vous suggère d'appeler la fonction "isInt" car elle ne compte pas les nombres décimaux comme – FluorescentGreen5

3

strtol arrête le premier non chiffres

mais si vous lisez la page de manuel http://man7.org/linux/man-pages/man3/strtol.3.html vous pouvez voir

If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, strtol() stores the original value of nptr in *endptr (and returns 0). In particular, if *nptr is not '\0' but **endptr is '\0' on return, the entire string is valid.

-à-dire

string testString = "ANYTHING"; 
cout << "testString = " << testString << endl; 
char *endptr; 
int testInt = strtol(testString.c_str(),&endptr,0); 
if(**endptr) 
    cout << "bad input"; 
+0

'erreur: type invalide argument de unaire '*' (avoir 'char')' et j'ai fait en sorte d'utiliser ce patch (tous les 3 fichiers): http://tehsausage.com/mingw-to-string – FluorescentGreen5

0

N'utilisez pas la solution C++ 11 avec des exceptions, car elle est plus lente. Voici une rapide la version C++ 11:

#include <algorithm> 
bool is_decimal(const std::string& s) 
{ 
    return !s.empty() && std::find_if(s.begin(), s.end(), [](char c){ return !std::isdigit(c); }) == s.end(); 
} 

Si vous êtes sûr que vos cordes la plupart du temps ne sont pas vides, vous pouvez supprimer s.empty()!. Si vous n'êtes pas, le garder parce ! S.empty() (! (S.length() == 0)) est moins cher que si vous appelez find_if (reference) avec une chaîne vide.

Éditer: Si vous devez gérer un débordement, utilisez la version d'exception ci-dessus. Seulement si vous ne pouvez pas utiliser des exceptions utilisent ceci:

#include <string> 
#include <sstream> 
#include <limits> 

template <class T> 
bool is_decimal_and_fit(const std::string& s) 
{ 
    long double decimal = 0; 
    return (!(std::istringstream(s) >> decimal).fail() && (decimal >= std::numeric_limits<T>::lowest()) && (decimal <= std::numeric_limits<T>::max())); 
} 
+0

Il vérifie juste s'il n'a que des chiffres. Je dois être en mesure de confirmer si le nombre déborde pour signé/non signé. Dois-je m'en tenir aux autres méthodes fournies? Il suffit d'utiliser – FluorescentGreen5

+0

ceci: bool is_decimal (const std :: string & s) { retour s.empty() && std :: find_if (s.begin(), de s.end(), [] (char c!) {return! std :: isdigit (c);}) == s.fin(); } – tobias88

+0

Désolé, Maj + Entrée ne fonctionne pas dans ce commentaire rapide Je ne peux pas le supprimer :) – tobias88