Come analizzare una stringa in un int in C ++?

Qual è il modo in cui C ++ analizza una stringa (fornita come char *) in un int? La gestione degli errori solida e chiara è un vantaggio (invece di restituire zero ).

Nel nuovo C ++ 11 ci sono funzioni per questo: stoi, stol, stoll, stoul e così via.

int myNr = std::stoi(myString); 

Genererà un’eccezione sull’errore di conversione.

Anche queste nuove funzioni hanno ancora lo stesso problema notato da Dan: convertiranno felicemente la stringa “11x” in intero “11”.

Vedi di più: http://en.cppreference.com/w/cpp/string/basic_string/stol

Cosa non fare

Ecco il mio primo consiglio: non usare stringstream per questo . Mentre all’inizio può sembrare semplice da usare, scoprirai che devi fare molto lavoro extra se vuoi robustezza e buona gestione degli errori.

Ecco un approccio che intuitivamente sembra come dovrebbe funzionare:

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

Questo ha un grosso problema: str2int(i, "1337h4x0r") restituirà felicemente true e str2int(i, "1337h4x0r") il valore 1337 . Possiamo aggirare questo problema assicurandoci che non ci siano più caratteri nello stringstream dopo la conversione:

 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; } 

Abbiamo risolto un problema, ma ci sono ancora un paio di altri problemi.

Cosa succede se il numero nella stringa non è base 10? Possiamo provare ad adattare altre basi impostando il stream sulla modalità corretta (ad es. ss << std::hex ) prima di provare la conversione. Ma questo significa che il chiamante deve sapere a priori quale base è il numero - e come può sapere chi lo chiama? Il chiamante non sa quale sia il numero ancora. Non sanno nemmeno che è un numero! Come possono aspettarsi di sapere quale base è? Potremmo semplicemente imporre che tutti i numeri inseriti nei nostri programmi devono essere di base 10 e rifiutare l'input esadecimale o ottale come non valido. Ma non è molto flessibile o robusto. Non esiste una soluzione semplice a questo problema. Non puoi semplicemente provare la conversione una volta per ogni base, perché la conversione decimale avrà sempre successo per i numeri ottali (con uno zero iniziale) e la conversione ottale potrebbe riuscire per alcuni numeri decimali. Quindi ora devi controllare per uno zero iniziale. Ma aspetta! I numeri esadecimali possono iniziare anche con uno zero iniziale (0x ...). Sospiro.

Anche se riesci ad affrontare i problemi sopra elencati, c'è ancora un altro problema più grande: cosa succede se il chiamante deve distinguere tra input errati (ad esempio "123foo") e un numero che è fuori dal range di int (es. "4000000000" per 32-bit int )? Con lo stringstream , non c'è modo di fare questa distinzione. Sappiamo solo se la conversione ha avuto esito positivo o negativo. Se fallisce, non abbiamo modo di sapere perché ha fallito. Come potete vedere, lo stringstream lascia molto a desiderare se volete robustezza e una chiara gestione degli errori.

Questo mi porta al mio secondo consiglio: non usare il lexical_cast di Boost per questo . Considera cosa ha da dire la documentazione di lexical_cast :

Laddove è richiesto un livello più elevato di controllo sulle conversioni, std :: stringstream e std :: wstringstream offrono un percorso più appropriato. Laddove sono richieste conversioni non basate sul stream, lexical_cast è lo strumento sbagliato per il lavoro e non è un caso speciale per tali scenari.

Che cosa?? Abbiamo già visto che lo stringstream ha uno scarso livello di controllo, eppure dice che lo stringstream dovrebbe essere usato al posto di lexical_cast se hai bisogno di "un più alto livello di controllo". Inoltre, dato che lexical_cast è solo un wrapper attorno a stringstream , soffre degli stessi problemi stringstream da stringstream : scarso supporto per più basi di numeri e scarsa gestione degli errori.

La migliore soluzione

Fortunatamente qualcuno ha già risolto tutti i problemi di cui sopra. La libreria standard C contiene strtol e famiglia che non presentano nessuno di questi problemi.

 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 == '\0' || *end != '\0') { return INCONVERTIBLE; } i = l; return SUCCESS; } 

Abbastanza semplice per qualcosa che gestisce tutti i casi di errore e supporta anche qualsiasi numero di base da 2 a 36. Se base è zero (il valore predefinito) proverà a convertire da qualsiasi base. Oppure il chiamante può fornire il terzo argomento e specificare che la conversione deve essere tentata solo per una determinata base. È robusto e gestisce tutti gli errori con un minimo sforzo.

Altri motivi per preferire strtol (e famiglia):

  • Presenta prestazioni di runtime molto migliori
  • Introduce un minore sovraccarico in fase di compilazione (gli altri tirano quasi 20 volte più SLOC dalle intestazioni)
  • Risulta nella dimensione del codice più piccola

Non c'è assolutamente nessuna buona ragione per usare qualsiasi altro metodo.

Questo è un modo C più sicuro di atoi ()

 const char* str = "123"; int i; if(sscanf(str, "%d", &i) == EOF ) { /* error */ } 

C ++ con stringstream di libreria standard: (grazie CMS )

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

Con la libreria boost : (grazie jk )

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

Modifica: risolta la versione di stringstream in modo che gestisse gli errori. (grazie a CMS e al commento di jk sul post originale)

Puoi usare il lexical_cast di Boost , che lo avvolge in un’interfaccia più generica. lexical_cast(Source) genera bad_lexical_cast in caso di errore.

Il buon vecchio modo C funziona ancora. Raccomando strtol o strtoul. Tra lo stato di ritorno e ‘endPtr’, puoi dare un buon risultato diagnostico. Gestisce anche basi multiple piacevolmente.

È ansible utilizzare un stringstream dal libraray standard C ++:

 stringstream ss(str); int x; ss >> x; if(ss) { // <-- error handling // use x } else { // not a number } 

Lo stato del stream verrà impostato in caso di errore se viene rilevato un numero non numerico durante il tentativo di leggere un numero intero.

Vedi i problemi di streaming per le insidie ​​di errorhandling e flussi in C ++.

Puoi usare lo stringstream

 int str2int (const string &str) { stringstream ss(str); int num; ss >> num; return num; } 

Penso che questi tre link riassumano:

le soluzioni stringstream e lexical_cast sono quasi le stesse del cast lessicale che usa stringstream.

Alcune specializzazioni di cast lessicali usano un approccio diverso, vedi http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp per i dettagli. Gli integer e i float sono ora specializzati per la conversione da stringa intera a intero.

Si può specializzare lexical_cast per i propri bisogni e renderlo veloce. Questa sarebbe la soluzione definitiva per soddisfare tutte le parti, pulita e semplice.

Gli articoli già menzionati mostrano il confronto tra diversi metodi di conversione degli interi <-> stringhe. Seguire gli approcci ha senso: vecchio c-way, spirit.karma, fastformat, semplice loop naïf.

Lexical_cast è ok in alcuni casi, ad esempio per la conversione da int a stringhe.

Convertire la stringa in int usando il cast lessicale non è una buona idea dato che è 10-40 volte più lento di atoi a seconda della piattaforma / compilatore usato.

Boost.Spirit.Karma sembra essere la libreria più veloce per convertire interi in stringa.

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

e il semplice semplice loop dall’articolo menzionato sopra è il modo più veloce per convertire string in int, ovviamente non il più sicuro, strtol () sembra una soluzione più sicura

 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; } 

La libreria C ++ String Toolkit Library (StrTk) ha la seguente soluzione:

 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 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(digit_table[static_cast(*it++)]); if (0xFF == digit) return false; v = (10 * v) + digit; } if (negative) v *= -1; return true; } 

InputIterator può essere di char non firmati *, char * o std :: string iterators e T dovrebbe essere un int signed, come signed int, int o long

Se hai C ++ 11, le soluzioni appropriate al giorno d’oggi sono le funzioni di conversione dell’intero C ++ in : stoi , stol , stoul , stoll , stoull . Lanciano le eccezioni appropriate quando danno un input errato e usano le funzioni fast e small strto* sotto il cofano.

Se si è bloccati con una revisione precedente di C ++, sarebbe forward-portable di voi per simulare queste funzioni nella vostra implementazione.

Da C ++ 17 in poi puoi usare std::from_chars dall’intestazione come documentato qui .

Per esempio:

 #include  #include  #include  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; } } 

Come bonus, può anche gestire altre basi, come esadecimale.

Mi piace la risposta di Dan Molding , aggiungerò solo un po ‘di stile C ++:

 #include  #include  #include  #include  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 != '\0') throw std::invalid_argument("toint: invalid string"); return result; } 

Funziona sia per std :: string che per const char * attraverso la conversione implicita. È utile anche per la conversione di base, ad es. to_int("0x7b") e to_int("0173") e to_int("01111011", 2) e to_int("0000007B", 16) e to_int("11120", 3) e to_int("3L", 34); restituirebbe 123.

A differenza di std::stoi funziona in pre-C ++ 11. Inoltre a differenza di std::stoi , boost::lexical_cast e stringstream genera eccezioni per stringhe strane come "123hohoho".

NB: Questa funzione tollera gli spazi to_int(" 123") ma non gli spazi finali, ovvero to_int(" 123") restituisce 123 mentre to_int("123 ") genera un'eccezione. Assicurati che sia accettabile per il tuo caso d'uso o regola il codice.

Tale funzione potrebbe far parte di STL ...

Conosco tre modi per convertire String in int:

Utilizzare la funzione stoi (String to int) o semplicemente andare con Stringstream, il terzo modo per eseguire la conversione individuale, il codice è qui sotto:

1o metodo

 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'; 

2 ° metodo

 #include  #include  #include  #include  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; } 

3 ° metodo - ma non per una conversione individuale

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

Mi piace la risposta di Dan , specialmente per evitare le eccezioni. Per lo sviluppo di sistemi embedded e altri sistemi di sviluppo di basso livello, potrebbe non esserci un adeguato framework di eccezioni disponibile.

Aggiunto un controllo per lo spazio bianco dopo una stringa valida … queste tre linee

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

Aggiunto un controllo per gli errori di analisi anche.

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

Ecco la funzione completa ..

 #include  #include  #include  #include  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 == '\0' || *end != '\0') { return INCONVERTIBLE; } return SUCCESS; } 

Potresti usare questo metodo definito.

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

E se dovessi convertire da String a Integer, dovresti fare quanto segue.

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

L'output sarebbe 102.

So che questa è una domanda più vecchia, ma l’ho incontrata così tante volte e, ad oggi, non ho ancora trovato una soluzione ben strutturata con le seguenti caratteristiche:

  • Può convertire qualsiasi base (e rilevare il tipo di base)
  • Rileverà dati errati (ovvero assicurerà che l’intera stringa, meno spazi bianchi iniziali / finali, venga consumata dalla conversione)
  • Garantirà che, indipendentemente dal tipo convertito in, l’intervallo del valore della stringa è accettabile.

Quindi, ecco il mio, con una cinghia di prova. Poiché utilizza le funzioni C strtoull / strtoll sotto il cofano, converte sempre prima il tipo più grande disponibile. Quindi, se non si utilizza il tipo più grande, eseguirà ulteriori controlli di intervallo per verificare che il proprio tipo non sia stato sottoposto a (sotto) stream. Per questo, è un po ‘meno performante che se si scelga correttamente strtol / strtoul. Tuttavia, funziona anche per cortometraggi / caratteri e, per quanto ne so, non esiste alcuna funzione di libreria standard che lo faccia anche.

Godere; spero che qualcuno lo trovi utile

 #include  #include  #include  #include  #include  static const int DefaultBase = 10; template static inline T CstrtoxllWrapper(const char *str, int base = DefaultBase) { while (isspace(*str)) str++; // remove leading spaces; verify there's data if (*str == '\0') { 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::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::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 != '\0') { throw std::invalid_argument("str; bad data"); } } return tmp; } template T StringToSigned(const char *str, int base = DefaultBase) { static const long long max = std::numeric_limits::max(); static const long long min = std::numeric_limits::min(); long long tmp = CstrtoxllWrapper(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 T StringToUnsigned(const char *str, int base = DefaultBase) { static const unsigned long long max = std::numeric_limits::max(); unsigned long long tmp = CstrtoxllWrapper(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 inline T StringToDecimal(const char *str, int base = DefaultBase) { return std::numeric_limits::is_signed ? StringToSigned(str, base) : StringToUnsigned(str, base); } template inline T StringToDecimal(T &out_convertedVal, const char *str, int base = DefaultBase) { return out_convertedVal = StringToDecimal(str, base); } /*============================== [ Test Strap ] ==============================*/ #include  #include  static bool _g_anyFailed = false; template 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(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, 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::max()); TEST(int8_t, "80", 16); TEST(int8_t, "-80", 16, true, std::numeric_limits::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::max()); TEST(uint8_t, "80", 16, true, std::numeric_limits::max()+1); TEST(uint8_t, "-80", 16); TEST(uint8_t, "-81", 16); TEST(uint8_t, "FF", 16, true, std::numeric_limits::max()); TEST(uint8_t, "100", 16); std::cout << "============ int16_t range tests ============" << std::endl; TEST(int16_t, "7FFF", 16, true, std::numeric_limits::max()); TEST(int16_t, "8000", 16); TEST(int16_t, "-8000", 16, true, std::numeric_limits::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::max()); TEST(uint16_t, "8000", 16, true, std::numeric_limits::max()+1); TEST(uint16_t, "-8000", 16); TEST(uint16_t, "-8001", 16); TEST(uint16_t, "FFFF", 16, true, std::numeric_limits::max()); TEST(uint16_t, "10000", 16); std::cout << "============ int32_t range tests ============" << std::endl; TEST(int32_t, "7FFFFFFF", 16, true, std::numeric_limits::max()); TEST(int32_t, "80000000", 16); TEST(int32_t, "-80000000", 16, true, std::numeric_limits::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::max()); TEST(uint32_t, "80000000", 16, true, std::numeric_limits::max()+1); TEST(uint32_t, "-80000000", 16); TEST(uint32_t, "-80000001", 16); TEST(uint32_t, "FFFFFFFF", 16, true, std::numeric_limits::max()); TEST(uint32_t, "100000000", 16); std::cout << "============ int64_t range tests ============" << std::endl; TEST(int64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits::max()); TEST(int64_t, "8000000000000000", 16); TEST(int64_t, "-8000000000000000", 16, true, std::numeric_limits::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::max()); TEST(uint64_t, "8000000000000000", 16, true, std::numeric_limits::max()+1); TEST(uint64_t, "-8000000000000000", 16); TEST(uint64_t, "-8000000000000001", 16); TEST(uint64_t, "FFFFFFFFFFFFFFFF", 16, true, std::numeric_limits::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 is the user-land method; it is overloaded so it can be called either like this:

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

o questo:

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

I hate repeating the int type, so prefer the latter. This ensures that if the type of 'a' changes one does not get bad results. I wish the compiler could figure it out like:

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

...but, C++ does not deduce template return types, so that's the best I can get.

The implementation is pretty simple:

CstrtoxllWrapper wraps both strtoull and strtoll , calling whichever is necessary based on the template type's signed-ness and providing some additional guarantees (eg negative input is disallowed if unsigned and it ensures the entire string was converted).

CstrtoxllWrapper is used by StringToSigned and StringToUnsigned with the largest type (long long/unsigned long long) available to the compiler; this allows the maximal conversion to be performsd. Then, if it is necessary, StringToSigned / StringToUnsigned performs the final range checks on the underlying type. Finally, the end-point method, StringToDecimal , decides which of the StringTo* template methods to call based on the underlying type's signed-ness.

I think most of the junk can be optimized out by the compiler; just about everything should be compile-time deterministic. Any commentary on this aspect would be interesting to me!

In C, you can use int atoi (const char * str) ,

Parses the C-string str interpreting its content as an integral number, which is returned as a value of type int.