Comment analyser une chaîne de caractères vers un int en C++?

Quelle est la façon C++ d'analyser une chaîne (donnée comme char *) dans un int? Un traitement des erreurs robuste et clair est un plus (au lieu de returning zero ).

250
demandé sur Eugene Yokota 2008-10-11 23:20:37

17 réponses

dans le nouveau c++11 Il y a des fonctions pour cela: stoi, Stoll, stoul et ainsi de suite.

int myNr = std::stoi(myString);

il va jeter une exception sur l'erreur de conversion.

même ces nouvelles fonctions ont encore le même numéro comme noté par Dan: ils seront heureux de convertir la chaîne de caractères" 11x "en entier"11".

voir plus: http://en.cppreference.com/w/cpp/string/basic_string/stol

152
répondu CC. 2012-08-29 19:21:18

que ne pas faire

Voici mon premier conseil: n'utilisez pas stringstream pour ce . Alors qu'au début, il peut sembler simple à utiliser, vous trouverez que vous avez à faire beaucoup de travail supplémentaire si vous voulez robustesse et une bonne manipulation des erreurs.

Voici une approche qui semble intuitivement devoir fonctionner:

bool str2int (int &i, char const *s)
{
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail()) {
        // not an integer
        return false;
    }
    return true;
}

cela a un problème majeur: str2int(i, "1337h4x0r") retournera heureux true et i obtiendront la valeur 1337 . Nous pouvons contourner ce problème en nous assurant qu'il n'y a plus de caractères dans le stringstream après la conversion:

bool str2int (int &i, char const *s)
{
    char              c;
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail() || ss.get(c)) {
        // not an integer
        return false;
    }
    return true;
}

nous avons corrigé un problème, mais il y a encore quelques autres problèmes.

et si le nombre dans la chaîne n'est pas la base 10? Nous pouvons essayer d'accommoder d'autres bases en réglant le flux sur le mode correct (par exemple ss << std::hex ) avant d'essayer le conversion. Mais cela signifie que l'appelant doit savoir a priori Quelle est la base du numéro -- et comment l'appelant peut-il le savoir? L'appelant ne sait pas encore quel est le numéro. Ils ne savent même pas qu'il est un nombre! Comment peuvent-ils savoir ce que c'est? Nous pourrions simplement exiger que tous les nombres entrés dans nos programmes doivent être la base 10 et rejeter les entrées hexadécimales ou octales comme invalides. Mais ce n'est pas très souple ou robuste. Y n'est pas une solution simple à ce problème. Vous ne pouvez pas simplement essayer la conversion une fois pour chaque base, parce que la conversion décimale réussira toujours pour les nombres octaux (avec un zéro principal) et la conversion octale peut réussir pour certains nombres décimaux. Alors maintenant, vous devez vérifier pour un zéro. Mais attendez! Les nombres hexadécimaux peuvent commencer avec un zéro principal aussi (0x...). Soupir.

même si vous réussissez à traiter les problèmes ci-dessus, il ya encore un autre plus grand problème: que se passe-t-il si l'appelant doit faire une distinction entre une mauvaise entrée (par exemple "123foo") et un numéro qui est hors de la gamme int (par exemple "4000000000" pour 32 bits int )? Avec stringstream , il n'y a aucun moyen de faire cette distinction. Nous savons seulement si la conversion a réussi ou échoué. Si elle échoue, nous n'avons aucun moyen de savoir pourquoi il a échoué. Comme vous pouvez le voir, stringstream laisse beaucoup à désirer si vous voulez la robustesse et la manipulation claire des erreurs.

cela m'amène à mon deuxième conseil: ne pas utiliser Boost lexical_cast pour ce . Considérez ce que la documentation lexical_cast a à dire:

où un degré de contrôle plus élevé est nécessaire sur les conversions, std:: stringstream et std:: wstringstream offre un plus chemin d'accès approprié. Où les conversions non basées sur les flux sont nécessaire, lexical_cast est le mal outil pour le travail et n'est pas spécial-étui pour de tels scénarios.

quoi?? Nous avons déjà vu que stringstream a un faible niveau de contrôle, et pourtant il dit stringstream devrait être utilisé au lieu de lexical_cast si vous avez besoin"d'un niveau de contrôle plus élevé". En outre, parce que lexical_cast est juste un enveloppeur autour de stringstream , il souffre des mêmes problèmes que stringstream fait: un faible soutien pour les bases de nombres multiples et une mauvaise gestion des erreurs.

le meilleur solution

Heureusement, quelqu'un a déjà résolu tous les problèmes ci-dessus. La bibliothèque standard C contient strtol et la famille qui n'ont aucun de ces problèmes.

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
    char *end;
    long  l;
    errno = 0;
    l = strtol(s, &end, base);
    if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
        return OVERFLOW;
    }
    if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
        return UNDERFLOW;
    }
    if (*s == '"151920920"' || *end != '"151920920"') {
        return INCONVERTIBLE;
    }
    i = l;
    return SUCCESS;
}

assez simple pour quelque chose qui gère tous les cas d'erreur et supporte aussi n'importe quelle base de nombre de 2 à 36. Si base est zéro (la valeur par défaut) il va essayer de convertir à partir de n'importe quelle base. Ou l'appelant peut fournir le troisième argument et spécifier que le la conversion ne devrait être tentée que pour une base particulière. Il est robuste et traite toutes les erreurs avec un minimum d'effort.

autres raisons de préférer strtol (et famille):

  • Il présente beaucoup mieux les performances d'exécution
  • il introduit moins de temps de compilation overhead (les autres tirent dans presque 20 fois plus de SLOC des en-têtes)
  • il en résulte la plus petite taille de code

il n'y a absolument aucune raison d'utiliser une autre méthode.

192
répondu Dan Moulding 2011-05-27 16:05:21

Ce qui est sûr C que de la manière atoi()

const char* str = "123";
int i;

if(sscanf(str, "%d", &i)  == EOF )
{
   /* error */
}

C++ avec bibliothèque standard stringstream : (merci CMS )

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR 
  }
  return num;
}

avec boost bibliothèque: (merci jk )

#include <boost/lexical_cast.hpp>
#include <string>

try
{
    std::string str = "123";
    int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
    // Error
}

Edit: Correction de la version stringstream pour qu'elle gère les erreurs. (merci à la CMS et jk commentaire sur le post original)

65
répondu Luka Marinko 2017-05-23 12:02:48

vous pouvez utiliser Boost's lexical_cast 151950920" , qui enveloppe ce dans une interface plus générique. lexical_cast<Target>(Source) lance bad_lexical_cast sur la panne.

22
répondu jk. 2017-05-23 12:26:35

la bonne vieille méthode fonctionne toujours. Je recommande strtol ou strtoul. Entre le statut de retour et le' endPtr', vous pouvez donner une bonne sortie de diagnostic. Il gère aussi plusieurs bases bien.

20
répondu Chris Arguin 2008-10-11 19:22:28

vous pouvez utiliser le stringstream de la bibliothèque standard C++:

stringstream ss(str);
int x;
ss >> x;

if(ss) { // <-- error handling
  // use x
} else {
  // not a number
}

l'état du flux sera réglé à fail si un chiffre est rencontrés lors de l' en essayant de lire un entier.

Voir Flux " pièges pour les pièges de la errorhandling et les ruisseaux en C++.

15
répondu jk. 2008-10-11 19:49:49

Vous pouvez utiliser stringstream

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}
10
répondu CMS 2008-10-11 19:26:06

je pense que ces trois liens résument:

les solutions stringstream et lexical_cast sont à peu près les mêmes que celles utilisées par lexical cast.

certaines spécialisations de la fonte lexicale utiliser une approche différente voir http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp pour plus de détails. Les entiers et les flotteurs sont maintenant spécialisés pour la conversion d'entier en chaîne.

on peut spécialisez lexical_cast pour ses propres besoins et faites-le rapidement. Ce serait la solution ultime satisfaisant toutes les parties, propre et simple.

les Articles déjà mentionnés montrent la comparaison entre les différentes méthodes de conversion des entiers <-> chaînes. Les approches suivantes ont du sens: vieux c-way, esprit.Karma, fastformat, simple boucle naïve.

Lexical_cast est correct dans certains cas, par exemple pour la conversion int en chaîne de caractères.

Convertir une chaîne en int en utilisant lexical cast n'est pas une bonne idée car il est 10-40 fois plus lent qu'atoi selon la plate-forme/compilateur utilisé.

coup de pouce.Esprit.Karma semble être la bibliothèque la plus rapide pour convertir entier en chaîne.

ex.: generate(ptr_char, int_, integer_number);

et boucle simple de base de l'article mentionné ci-dessus est un moyen le plus rapide pour convertir chaîne en int, évidemment pas le plus sûr, strtol() semble comme une solution plus sûre

int naive_char_2_int(const char *p) {
    int x = 0;
    bool neg = false;
    if (*p == '-') {
        neg = true;
        ++p;
    }
    while (*p >= '0' && *p <= '9') {
        x = (x*10) + (*p - '0');
        ++p;
    }
    if (neg) {
        x = -x;
    }
    return x;
}
7
répondu caa 2011-08-05 20:20:56

Le Chaîne C++ Bibliothèque d'outils (StrTk) a la solution suivante:

static const std::size_t digit_table_symbol_count = 256;
static const unsigned char digit_table[digit_table_symbol_count] = {
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 - 0x0F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 - 0x17
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 - 0x1F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 - 0x27
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x28 - 0x2F
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
   0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 - 0x3F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 - 0x47
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x48 - 0x4F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 - 0x57
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 - 0x5F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 - 0x67
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x68 - 0x6F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x70 - 0x77
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 - 0x7F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x80 - 0x87
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x88 - 0x8F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x90 - 0x97
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x98 - 0x9F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA0 - 0xA7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA8 - 0xAF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB0 - 0xB7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB8 - 0xBF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC0 - 0xC7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC8 - 0xCF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD0 - 0xD7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD8 - 0xDF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 - 0xE7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE8 - 0xEF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xF0 - 0xF7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 0xF8 - 0xFF
 };

template<typename InputIterator, typename T>
inline bool string_to_signed_type_converter_impl_itr(InputIterator begin, InputIterator end, T& v)
{
   if (0 == std::distance(begin,end))
      return false;
   v = 0;
   InputIterator it = begin;
   bool negative = false;
   if ('+' == *it)
      ++it;
   else if ('-' == *it)
   {
      ++it;
      negative = true;
   }
   if (end == it)
      return false;
   while(end != it)
   {
      const T digit = static_cast<T>(digit_table[static_cast<unsigned int>(*it++)]);
      if (0xFF == digit)
         return false;
      v = (10 * v) + digit;
   }
   if (negative)
      v *= -1;
   return true;
}

La InputIterator peut être soit de type unsigned char*, char* ou std::string itérateurs, et T devrait être signé int, comme signé int, int, long ou

7
répondu Rup 2018-06-25 14:30:32

si vous avez C++11, les solutions appropriées de nos jours sont les fonctions de conversion <string> : stoi , stol , stoul , stoll , stoull . Ils jettent des exceptions appropriées quand donné entrée incorrecte et d'utiliser le rapide et petit strto* fonctions sous le capot.

si vous êtes bloqué avec une révision antérieure de C++, il serait forward-portable de vous pour imiter ces fonctions dans votre implémentation.

6
répondu fuzzyTew 2013-08-01 15:52:03

à partir de C++17, Vous pouvez utiliser std::from_chars de l'en-tête <charconv> comme documenté ici .

par exemple:

#include <iostream>
#include <charconv>
#include <array>

int main()
{
    char const * str = "42";
    int value = 0;

    std::from_chars_result result = std::from_chars(std::begin(str), std::end(str), value);

    if(result.error == std::errc::invalid_argument)
    {
      std::cout << "Error, invalid format";
    }
    else if(result.error == std::errc::result_out_of_range)
    {
      std::cout << "Error, value too big for int range";
    }
    else
    {
      std::cout << "Success: " << result;
    }
}

en bonus, il peut également gérer d'autres bases, comme hexadécimal.

3
répondu Pharap 2018-07-13 17:19:07

j'aime Dan Moulage de réponse , je vais juste ajouter un peu de C++ du style:

#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

int to_int(const std::string &s, int base = 0)
{
    char *end;
    errno = 0;
    long result = std::strtol(s.c_str(), &end, base);
    if (errno == ERANGE || result > INT_MAX || result < INT_MIN)
        throw std::out_of_range("toint: string is out of range");
    if (s.length() == 0 || *end != '"151900920"')
        throw std::invalid_argument("toint: invalid string");
    return result;
}

il fonctionne à la fois pour std::string et const char* par la conversion implicite. Il est également utile pour la conversion de base, par exemple tous to_int("0x7b") et to_int("0173") et to_int("01111011", 2) et to_int("0000007B", 16) et to_int("11120", 3) et to_int("3L", 34); retournerait 123.

contrairement à std::stoi il fonctionne en pré-C++11. De plus, contrairement à std::stoi , boost::lexical_cast et stringstream il lève des exceptions pour bizarre comme les chaînes "123hohoho".

NB: cette fonction tolère les espaces de tête mais pas les espaces de fuite, c'est-à-dire que to_int(" 123") renvoie 123 tandis que to_int("123 ") renvoie l'exception. Assurez-vous que cela est acceptable pour votre cas d'utilisation ou ajustez le code.

une telle fonction pourrait faire partie du STL...

2
répondu user3925906 2014-08-09 21:20:27

je connais trois façons de convertir une chaîne en int:

soit utiliser la fonction stoi(String to int) ou tout simplement aller avec Stringstream, la troisième façon d'aller la conversion individuelle, Code est ci-dessous:

1ère méthode

std::string s1 = "4533";
std::string s2 = "3.010101";
std::string s3 = "31337 with some string";

int myint1 = std::stoi(s1);
int myint2 = std::stoi(s2);
int myint3 = std::stoi(s3);

std::cout <<  s1 <<"=" << myint1 << '\n';
std::cout <<  s2 <<"=" << myint2 << '\n';
std::cout <<  s3 <<"=" << myint3 << '\n';

2e méthode

#include <string.h>
#include <sstream>
#include <iostream>
#include <cstring>
using namespace std;


int StringToInteger(string NumberAsString)
{
    int NumberAsInteger;
    stringstream ss;
    ss << NumberAsString;
    ss >> NumberAsInteger;
    return NumberAsInteger;
}
int main()
{
    string NumberAsString;
    cin >> NumberAsString;
    cout << StringToInteger(NumberAsString) << endl;
    return 0;
} 

troisième méthode - mais pas pour une conversion individuelle

std::string str4 = "453";
int i = 0, in=0; // 453 as on
for ( i = 0; i < str4.length(); i++)
{

    in = str4[i];
    cout <<in-48 ;

}
2
répondu Iqra. 2017-05-31 03:18:24

j'aime Dan la réponse de , esp en raison de l'évitement des exceptions. Dans le cas du développement de systèmes intégrés et d'autres développements de systèmes de faible niveau, il n'existe peut-être pas de cadre D'Exception adéquat.

a ajouté un contrôle pour l'espace blanc après une chaîne valide...ces trois lignes

    while (isspace(*end)) {
        end++;
    }



Ajouté un contrôle pour les erreurs d'analyse aussi.

    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }



Ici est la fonction complète..

#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2long (long &l, char const *s, int base = 0)
{
    char *end = (char *)s;
    errno = 0;

    l = strtol(s, &end, base);

    if ((errno == ERANGE) && (l == LONG_MAX)) {
        return OVERFLOW;
    }
    if ((errno == ERANGE) && (l == LONG_MIN)) {
        return UNDERFLOW;
    }
    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }    
    while (isspace((unsigned char)*end)) {
        end++;
    }

    if (*s == '"151920920"' || *end != '"151920920"') {
        return INCONVERTIBLE;
    }

    return SUCCESS;
}
1
répondu pellucide 2017-05-23 12:10:45

, Vous pouvez utiliser cette méthode définie.

#define toInt(x) {atoi(x.c_str())};

et si vous deviez passer D'une chaîne à un entier, vous feriez juste ce qui suit.

int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

la sortie serait 102.

0
répondu Boris 2014-12-31 17:30:29

je sais qu'il s'agit d'une question plus ancienne, mais je l'ai rencontrée tellement de fois et, à ce jour, n'ont toujours pas trouvé une solution bien modelée ayant les caractéristiques suivantes:

  • peut convertir n'importe quelle base (et détecter le type de base)
  • détectera les données erronées (c.-à-d. s'assurer que la chaîne entière, moins d'espace de blanc menant/traînant, est consommée par la conversion)
  • garantira que, quel que soit le type converti, la plage de la valeur de la chaîne est acceptable.

donc, voici le mien, avec une sangle d'essai. Parce qu'il utilise les fonctions C strtoull/strtoll sous la hotte, il convertit toujours en premier au plus grand type disponible. Ensuite, si vous n'utilisez pas le plus grand type, il effectuera des vérifications supplémentaires de portée pour vérifier que votre type n'a pas été(sous)débité. Pour cela, il est un peu moins performant que si l'on choisissait correctement strtol/strtoul. Cependant, il a également travaille pour des shorts / chars et, à ma connaissance, il n'existe aucune fonction de bibliothèque standard qui fait cela, aussi.

Profiter; j'espère que quelqu'un le trouve utile.

#include <cstdlib>
#include <cerrno>
#include <limits>
#include <stdexcept>
#include <sstream>

static const int DefaultBase = 10;

template<typename T>
static inline T CstrtoxllWrapper(const char *str, int base = DefaultBase)
{
    while (isspace(*str)) str++; // remove leading spaces; verify there's data
    if (*str == '"151900920"') { throw std::invalid_argument("str; no data"); } // nothing to convert

    // NOTE:  for some reason strtoull allows a negative sign, we don't; if
    //          converting to an unsigned then it must always be positive!
    if (!std::numeric_limits<T>::is_signed && *str == '-')
    { throw std::invalid_argument("str; negative"); }

    // reset errno and call fn (either strtoll or strtoull)
    errno = 0;
    char *ePtr;
    T tmp = std::numeric_limits<T>::is_signed ? strtoll(str, &ePtr, base)
                                              : strtoull(str, &ePtr, base);

    // check for any C errors -- note these are range errors on T, which may
    //   still be out of the range of the actual type we're using; the caller
    //   may need to perform additional range checks.
    if (errno != 0) 
    {
            if (errno == ERANGE) { throw std::range_error("str; out of range"); }
            else if (errno == EINVAL) { throw std::invalid_argument("str; EINVAL"); }
            else { throw std::invalid_argument("str; unknown errno"); }
    }

    // verify everything converted -- extraneous spaces are allowed
    if (ePtr != NULL)
    {
            while (isspace(*ePtr)) ePtr++;
            if (*ePtr != '"151900920"') { throw std::invalid_argument("str; bad data"); }
    }

    return tmp;
}

template<typename T>
T StringToSigned(const char *str, int base = DefaultBase)
{
    static const long long max = std::numeric_limits<T>::max();
    static const long long min = std::numeric_limits<T>::min();

    long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp < min || tmp > max)
    {
            std::ostringstream err;
            err << "str; value " << tmp << " out of " << sizeof(T) * 8
                << "-bit signed range (";
            if (sizeof(T) != 1) err << min << ".." << max;
            else err << (int) min << ".." << (int) max;  // don't print garbage chars
            err << ")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
T StringToUnsigned(const char *str, int base = DefaultBase)
{
    static const unsigned long long max = std::numeric_limits<T>::max();

    unsigned long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp > max)
    {
            std::ostringstream err;
            err << "str; value " << tmp << " out of " << sizeof(T) * 8
                << "-bit unsigned range (0..";
            if (sizeof(T) != 1) err << max;
            else err << (int) max;  // don't print garbage chars
            err << ")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
inline T
StringToDecimal(const char *str, int base = DefaultBase)
{
    return std::numeric_limits<T>::is_signed ? StringToSigned<T>(str, base)
                                             : StringToUnsigned<T>(str, base);
}

template<typename T>
inline T
StringToDecimal(T &out_convertedVal, const char *str, int base = DefaultBase)
{
    return out_convertedVal = StringToDecimal<T>(str, base);
}

/*============================== [ Test Strap ] ==============================*/ 

#include <inttypes.h>
#include <iostream>

static bool _g_anyFailed = false;

template<typename T>
void TestIt(const char *tName,
            const char *s, int base,
            bool successExpected = false, T expectedValue = 0)
{
    #define FAIL(s) { _g_anyFailed = true; std::cout << s; }

    T x;
    std::cout << "converting<" << tName << ">b:" << base << " [" << s << "]";
    try
    {
            StringToDecimal<T>(x, s, base);
            // get here on success only
            if (!successExpected)
            {
                    FAIL(" -- TEST FAILED; SUCCESS NOT EXPECTED!" << std::endl);
            }
            else
            {
                    std::cout << " -> ";
                    if (sizeof(T) != 1) std::cout << x;
                    else std::cout << (int) x;  // don't print garbage chars
                    if (x != expectedValue)
                    {
                            FAIL("; FAILED (expected value:" << expectedValue << ")!");
                    }
                    std::cout << std::endl;
            }
    }
    catch (std::exception &e)
    {
            if (successExpected)
            {
                    FAIL(   " -- TEST FAILED; EXPECTED SUCCESS!"
                         << " (got:" << e.what() << ")" << std::endl);
            }
            else
            {
                    std::cout << "; expected exception encounterd: [" << e.what() << "]" << std::endl;
            }
    }
}

#define TEST(t, s, ...) \
    TestIt<t>(#t, s, __VA_ARGS__);

int main()
{
    std::cout << "============ variable base tests ============" << std::endl;
    TEST(int, "-0xF", 0, true, -0xF);
    TEST(int, "+0xF", 0, true, 0xF);
    TEST(int, "0xF", 0, true, 0xF);
    TEST(int, "-010", 0, true, -010);
    TEST(int, "+010", 0, true, 010);
    TEST(int, "010", 0, true, 010);
    TEST(int, "-10", 0, true, -10);
    TEST(int, "+10", 0, true, 10);
    TEST(int, "10", 0, true, 10);

    std::cout << "============ base-10 tests ============" << std::endl;
    TEST(int, "-010", 10, true, -10);
    TEST(int, "+010", 10, true, 10);
    TEST(int, "010", 10, true, 10);
    TEST(int, "-10", 10, true, -10);
    TEST(int, "+10", 10, true, 10);
    TEST(int, "10", 10, true, 10);
    TEST(int, "00010", 10, true, 10);

    std::cout << "============ base-8 tests ============" << std::endl;
    TEST(int, "777", 8, true, 0777);
    TEST(int, "-0111 ", 8, true, -0111);
    TEST(int, "+0010 ", 8, true, 010);

    std::cout << "============ base-16 tests ============" << std::endl;
    TEST(int, "DEAD", 16, true, 0xDEAD);
    TEST(int, "-BEEF", 16, true, -0xBEEF);
    TEST(int, "+C30", 16, true, 0xC30);

    std::cout << "============ base-2 tests ============" << std::endl;
    TEST(int, "-10011001", 2, true, -153);
    TEST(int, "10011001", 2, true, 153);

    std::cout << "============ irregular base tests ============" << std::endl;
    TEST(int, "Z", 36, true, 35);
    TEST(int, "ZZTOP", 36, true, 60457993);
    TEST(int, "G", 17, true, 16);
    TEST(int, "H", 17);

    std::cout << "============ space deliminated tests ============" << std::endl;
    TEST(int, "1337    ", 10, true, 1337);
    TEST(int, "   FEAD", 16, true, 0xFEAD);
    TEST(int, "   0711   ", 0, true, 0711);

    std::cout << "============ bad data tests ============" << std::endl;
    TEST(int, "FEAD", 10);
    TEST(int, "1234 asdfklj", 10);
    TEST(int, "-0xF", 10);
    TEST(int, "+0xF", 10);
    TEST(int, "0xF", 10);
    TEST(int, "-F", 10);
    TEST(int, "+F", 10);
    TEST(int, "12.4", 10);
    TEST(int, "ABG", 16);
    TEST(int, "10011002", 2);

    std::cout << "============ int8_t range tests ============" << std::endl;
    TEST(int8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(int8_t, "80", 16);
    TEST(int8_t, "-80", 16, true, std::numeric_limits<int8_t>::min());
    TEST(int8_t, "-81", 16);
    TEST(int8_t, "FF", 16);
    TEST(int8_t, "100", 16);

    std::cout << "============ uint8_t range tests ============" << std::endl;
    TEST(uint8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(uint8_t, "80", 16, true, std::numeric_limits<int8_t>::max()+1);
    TEST(uint8_t, "-80", 16);
    TEST(uint8_t, "-81", 16);
    TEST(uint8_t, "FF", 16, true, std::numeric_limits<uint8_t>::max());
    TEST(uint8_t, "100", 16);

    std::cout << "============ int16_t range tests ============" << std::endl;
    TEST(int16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(int16_t, "8000", 16);
    TEST(int16_t, "-8000", 16, true, std::numeric_limits<int16_t>::min());
    TEST(int16_t, "-8001", 16);
    TEST(int16_t, "FFFF", 16);
    TEST(int16_t, "10000", 16);

    std::cout << "============ uint16_t range tests ============" << std::endl;
    TEST(uint16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(uint16_t, "8000", 16, true, std::numeric_limits<int16_t>::max()+1);
    TEST(uint16_t, "-8000", 16);
    TEST(uint16_t, "-8001", 16);
    TEST(uint16_t, "FFFF", 16, true, std::numeric_limits<uint16_t>::max());
    TEST(uint16_t, "10000", 16);

    std::cout << "============ int32_t range tests ============" << std::endl;
    TEST(int32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(int32_t, "80000000", 16);
    TEST(int32_t, "-80000000", 16, true, std::numeric_limits<int32_t>::min());
    TEST(int32_t, "-80000001", 16);
    TEST(int32_t, "FFFFFFFF", 16);
    TEST(int32_t, "100000000", 16);

    std::cout << "============ uint32_t range tests ============" << std::endl;
    TEST(uint32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(uint32_t, "80000000", 16, true, std::numeric_limits<int32_t>::max()+1);
    TEST(uint32_t, "-80000000", 16);
    TEST(uint32_t, "-80000001", 16);
    TEST(uint32_t, "FFFFFFFF", 16, true, std::numeric_limits<uint32_t>::max());
    TEST(uint32_t, "100000000", 16);

    std::cout << "============ int64_t range tests ============" << std::endl;
    TEST(int64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(int64_t, "8000000000000000", 16);
    TEST(int64_t, "-8000000000000000", 16, true, std::numeric_limits<int64_t>::min());
    TEST(int64_t, "-8000000000000001", 16);
    TEST(int64_t, "FFFFFFFFFFFFFFFF", 16);
    TEST(int64_t, "10000000000000000", 16);

    std::cout << "============ uint64_t range tests ============" << std::endl;
    TEST(uint64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(uint64_t, "8000000000000000", 16, true, std::numeric_limits<int64_t>::max()+1);
    TEST(uint64_t, "-8000000000000000", 16);
    TEST(uint64_t, "-8000000000000001", 16);
    TEST(uint64_t, "FFFFFFFFFFFFFFFF", 16, true, std::numeric_limits<uint64_t>::max());
    TEST(uint64_t, "10000000000000000", 16);

    std::cout << std::endl << std::endl
              << (_g_anyFailed ? "!! SOME TESTS FAILED !!" : "ALL TESTS PASSED")
              << std::endl;

    return _g_anyFailed;
}

StringToDecimal est la méthode de l'utilisateur-terre; il est surchargé de sorte qu'il peut être appelé comme ceci:

int a; a = StringToDecimal<int>("100");

ou ceci:

int a; StringToDecimal(a, "100");

je déteste répéter le type int, donc préférez le dernier. Cela garantit que si le type de " a " Change, on n'obtient pas de mauvais résultats. J'aimerais que le compilateur puisse le comprendre comme:

int a; a = StringToDecimal("100");

...mais, C++ ne permet pas de déduire les types de retour de template, donc c'est le meilleur que je puisse obtenir.

la mise en œuvre est assez simple:

CstrtoxllWrapper enveloppe à la fois strtoull et strtoll , en appelant au besoin selon le type de modèle signé et en fournissant quelques garanties supplémentaires (par exemple, l'entrée négative est refusée si elle n'est pas signée et garantit que toute la chaîne a été convertie).

CstrtoxllWrapper est utilisé par StringToSigned et StringToUnsigned avec le plus grand type (long long/non signé long) Disponible pour le compilateur; cela permet la conversion maximale à effectuer. Ensuite, s'il y a lieu, StringToSigned / StringToUnsigned effectue les derniers contrôles de portée sur le type sous-jacent. Enfin, c'est la méthode du point final, StringToDecimal , qui détermine lequel des StringTo * template méthodes d'appel basées sur la signature du type sous-jacent.

je pense que la plupart de la camelote peut être optimisée par le compilateur; à peu près tout devrait être déterministe de temps de compilation. Aucun commentaire sur cet aspect serait intéressant pour moi!

0
répondu DreamWarrior 2015-07-03 00:41:05

en C, vous pouvez utiliser int atoi (const char * str) ,

analyse la chaîne de caractères C en interprétant son contenu comme un nombre intégral, qui est retourné comme une valeur de type int.

-3
répondu BlackMamba 2013-08-01 16:01:15