Qual è il modo C ++ di analizzare una stringa (data come char *) in un int? Una gestione degli errori chiara e affidabile è un vantaggio (anziché restituire zero ).
Qual è il modo C ++ di analizzare una stringa (data come char *) in un int? Una gestione degli errori chiara e affidabile è un vantaggio (anziché restituire zero ).
Risposte:
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 rilevato da Dan: convertiranno felicemente la stringa "11x" in numero intero "11".
Scopri di più: http://en.cppreference.com/w/cpp/string/basic_string/stol
size_t
non è uguale alla lunghezza della stringa, si è interrotto in anticipo. Restituirà comunque 11 in quel caso, ma pos
sarà 2 invece della lunghezza della stringa 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29
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 che 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")
tornerà felicemente true
e i
otterrà il valore 1337
. Possiamo aggirare questo problema assicurandoci che non ci siano più caratteri nel 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 accogliere altre basi impostando lo 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ò forse chiamarlo? Il chiamante non sa ancora quale sia il numero. Non sanno nemmeno che lo siaun numero! Come ci si può aspettare che sappiano che base è? Potremmo semplicemente imporre che tutti i numeri immessi nei nostri programmi debbano essere base 10 e rifiutare l'input esadecimale o ottale come non valido. Ma questo 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 avere successo per alcuni numeri decimali. Quindi ora devi controllare lo zero iniziale. Ma aspetta! I numeri esadecimali possono iniziare anche con uno zero iniziale (0x ...). Sospiro.
Anche se riesci a gestire i problemi di cui sopra, c'è ancora un altro problema più grande: cosa succede se il chiamante deve distinguere tra input errato (ad es. "123foo") e un numero che non rientra nell'intervallo di int
(ad es. "4000000000" per 32 bit int
)? Con 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é abbia fallito. Come puoi vedere, stringstream
lascia molto a desiderare se vuoi robustezza e chiara gestione degli errori.
Questo mi porta al mio secondo consiglio: non usare Boost lexical_cast
per questo . Considera cosa lexical_cast
dice la documentazione:
Laddove è richiesto un maggiore livello di controllo sulle conversioni, std :: stringstream e std :: wstringstream offrono un percorso più appropriato. Laddove sono richieste conversioni non basate su stream, lexical_cast è lo strumento sbagliato per il lavoro e non è sottoposto a casi speciali per tali scenari.
Che cosa?? Abbiamo già visto che stringstream
ha un basso livello di controllo, eppure dice che stringstream
dovrebbe essere usato invece che lexical_cast
se hai bisogno di "un livello di controllo più alto". Inoltre, poiché lexical_cast
è solo un wrapper in giro stringstream
, soffre degli stessi problemi che stringstream
presenta: scarso supporto per più basi di numeri e scarsa gestione degli errori.
Fortunatamente, qualcuno ha già risolto tutti i problemi di cui sopra. La libreria standard C contiene una strtol
famiglia che non presenta 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 base numerica da 2 a 36. Se base
è zero (impostazione predefinita) 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 base particolare. È robusto e gestisce tutti gli errori con un minimo sforzo.
Altri motivi per preferire strtol
(e la famiglia):
Non c'è assolutamente alcun buon motivo per usare qualsiasi altro metodo.
strtol
deve essere thread-safe. POSIX richiede inoltre errno
di utilizzare l'archiviazione locale thread. Anche su sistemi non POSIX, quasi tutte le implementazioni errno
su sistemi multithread utilizzano l'archiviazione thread-local. L'ultimo standard C ++ richiede la errno
conformità POSIX. L'ultimo standard C richiede inoltre errno
di disporre di un archivio locale thread. Anche su Windows, che non è assolutamente conforme a POSIX, errno
è thread-safe e, per estensione, lo è anche strtol
.
std::stol
per questo, che genererà opportunamente eccezioni anziché restituire costanti.
std::stol
ancora di essere aggiunto al linguaggio C ++. Detto questo, non credo sia giusto dire che si tratta di "codifica C in C ++". È sciocco dire che std::strtol
è la codifica C quando fa esplicitamente parte del linguaggio C ++. La mia risposta si applicava perfettamente al C ++ quando è stato scritto e si applica anche al nuovo std::stol
. La chiamata di funzioni che possono generare eccezioni non è sempre la migliore per ogni situazione di programmazione.
Questo è un modo C più sicuro di atoi ()
const char* str = "123";
int i;
if(sscanf(str, "%d", &i) == EOF )
{
/* error */
}
C ++ con stringhe di libreria standard : (grazie CMS )
int str2int (const string &str) {
stringstream ss(str);
int num;
if((ss >> num).fail())
{
//ERROR
}
return num;
}
Con libreria boost : (grazie 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
}
Modifica: corretta la versione di Stringstream in modo che gestisse gli errori. (grazie al commento di CMS e jk sul post originale)
Il buon vecchio modo di C funziona ancora. Raccomando strtol o strtoul. Tra lo stato di ritorno e 'endPtr', è possibile fornire un buon output diagnostico. Gestisce bene anche più basi.
Puoi usare Boostlexical_cast
, che lo avvolge in un'interfaccia più generica.
lexical_cast<Target>(Source)
genera bad_lexical_cast
fallimento.
È possibile utilizzare a 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 flusso verrà impostato in modo da non riuscire se viene rilevata una non cifra durante il tentativo di leggere un numero intero.
Vedi le insidie dello streaming per le insidie della gestione degli errori e dei flussi in C ++.
Puoi usare stringstream
int str2int (const string &str) {
stringstream ss(str);
int num;
ss >> num;
return num;
}
Penso che questi tre link lo riassumano:
Le soluzioni stringstream e lexical_cast sono quasi uguali a quelle del cast lessicale che utilizza stringstream.
Alcune specializzazioni del cast lessicale usano un approccio diverso, vedi http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp per i dettagli. I numeri interi e float sono ora specializzati per la conversione da intero a stringa.
Si può specializzare lexical_cast per le proprie esigenze e renderlo veloce. Questa sarebbe la soluzione definitiva in grado di soddisfare tutte le parti, pulita e semplice.
Gli articoli già menzionati mostrano il confronto tra diversi metodi di conversione di stringhe di interi <->. I seguenti approcci hanno un senso: vecchio c-way, spirit.karma, fastformat, semplice ciclo ingenuo.
Lexical_cast è ok in alcuni casi, ad esempio per la conversione da int a stringa.
Convertire una stringa in int usando il cast lessicale non è una buona idea in quanto è 10-40 volte più lento di atoi a seconda della piattaforma / compilatore utilizzato.
Boost.Spirit.Karma sembra essere la libreria più veloce per convertire numeri interi in stringhe.
ex.: generate(ptr_char, int_, integer_number);
e il semplice loop di base dell'articolo menzionato sopra è un 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 (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<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;
}
InputIterator può essere di tipo unsigned char *, char * o std :: string iterators e si prevede che T sia un int con segno, come se int, int o long
v = (10 * v) + digit;
trabocca inutilmente con l'input di stringa con il valore di testo di INT_MIN
. La tabella ha un valore discutibile vs semplicementedigit >= '0' && digit <= '9'
Se si dispone di C ++ 11, le soluzioni appropriate al giorno d'oggi sono il C ++ intero funzioni di conversione in <string>
: stoi
, stol
, stoul
, stoll
, stoull
. Fanno le opportune eccezioni quando ricevono input errati e usano le strto*
funzioni veloci e piccole sotto il cofano.
Se sei bloccato con una revisione precedente di C ++, sarebbe imbarazzante per te imitare queste funzioni nella tua implementazione.
Da C ++ 17 in poi puoi usare std::from_chars
dall'intestazione <charconv>
come documentato qui .
Per esempio:
#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;
}
}
Come bonus, può anche gestire altre basi, come esadecimale.
Mi piace la risposta di Dan Moulding , aggiungerò solo un po 'di stile C ++:
#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 != '\0')
throw std::invalid_argument("toint: invalid string");
return result;
}
Funziona sia con std :: string che con const char * attraverso la conversione implicita. È anche utile per la conversione di base, ad esempio all 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);
restituire 123.
Diversamente std::stoi
funziona in pre-C ++ 11. Anche a differenza di std::stoi
, boost::lexical_cast
estringstream
genera eccezioni per strane stringhe come "123hohoho".
NB: questa funzione tollera gli spazi iniziali ma non gli spazi finali, ovvero to_int(" 123")
restituisce 123 mentre to_int("123 ")
genera un'eccezione. Assicurati che questo sia accettabile per il tuo caso d'uso o modifica il codice.
Tale funzione potrebbe far parte di STL ...
Conosco tre modi per convertire String in int:
Utilizza la funzione stoi (String to int) o semplicemente usa Stringstream, il terzo modo per passare alla conversione individuale, il codice è 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';
2o metodo
#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;
}
3o 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 <<in-48 ;
}
Mi piace la risposta di Dan , specialmente per evitare le eccezioni. Per lo sviluppo di sistemi embedded e altri sistemi di basso livello, potrebbe non essere disponibile un framework di eccezioni adeguato.
Aggiunto un controllo per gli spazi bianchi dopo una stringa valida ... queste tre righe
while (isspace(*end)) {
end++;
}
Aggiunto un controllo anche per gli errori di analisi.
if ((errno != 0) || (s == end)) {
return INCONVERTIBLE;
}
Ecco la funzione completa ..
#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 == '\0' || *end != '\0') {
return INCONVERTIBLE;
}
return SUCCESS;
}
" "
. strtol()
non è specificato per l'impostazione errno
quando non si verifica alcuna conversione. Meglio usare if (s == end) return INCONVERTIBLE;
per rilevare nessuna conversione. E poi if (*s == '\0' || *end != '\0')
può semplificare a if (*end)
2) || l > LONG_MAX
e || l < LONG_MIN
non servire a nulla - non sono mai veri.
È possibile utilizzare questo metodo definito.
#define toInt(x) {atoi(x.c_str())};
E se dovessi convertire da String a un numero intero, dovresti semplicemente fare quanto segue.
int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}
L'output sarebbe 102.
atoi
non sembra "il modo C ++", alla luce di altre risposte come quella accettata std::stoi()
.
So che questa è una domanda più vecchia, ma l'ho trovata tante volte e, ad oggi, non ho ancora trovato una soluzione ben modellata con le seguenti caratteristiche:
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 tipo non sia stato superato (sotto). Per questo, è un po 'meno performante di se si scegliesse correttamente strtol / strtoul. Tuttavia, funziona anche per cortometraggi / caratteri e, per quanto ne so, non esiste alcuna funzione di libreria standard che lo faccia.
Godere; si spera che qualcuno lo trovi 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 == '\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<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 != '\0') { 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
è il metodo terra dell'utente; è sovraccarico, quindi può essere chiamato in questo modo:
int a; a = StringToDecimal<int>("100");
o questo:
int a; StringToDecimal(a, "100");
Odio ripetere il tipo int, quindi preferisco quest'ultimo. Ciò garantisce che se il tipo di "a" cambia, non si ottengono risultati negativi. Vorrei che il compilatore potesse capirlo come:
int a; a = StringToDecimal("100");
... ma C ++ non deduce i tipi di restituzione del modello, quindi è il migliore che posso ottenere.
L'implementazione è piuttosto semplice:
CstrtoxllWrapper
esegue il wrapping di entrambi strtoull
e strtoll
, chiamando qualunque sia necessario in base alla firma del tipo di modello e fornendo alcune garanzie aggiuntive (ad esempio, l'input negativo non è consentito se non firmato e garantisce che l'intera stringa sia stata convertita).
CstrtoxllWrapper
è utilizzato da StringToSigned
e StringToUnsigned
con il tipo più grande (long long / unsigned long long) disponibile per il compilatore; ciò consente di eseguire la conversione massima. Quindi, se necessario, StringToSigned
/ StringToUnsigned
esegue i controlli dell'intervallo finale sul tipo sottostante. Infine, il metodo end-point,StringToDecimal
, decide quale dei metodi del modello StringTo * chiamare in base alla firma del tipo sottostante.
Penso che la maggior parte della spazzatura possa essere ottimizzata dal compilatore; quasi tutto dovrebbe essere deterministico in fase di compilazione. Qualsiasi commento su questo aspetto sarebbe interessante per me!
long long
invece di intmax_t
?
if (ePtr != str)
. Inoltre, utilizzare isspace((unsigned char) *ePtr)
per gestire correttamente i valori negativi di *ePtr
.
In C, puoi usare int atoi (const char * str)
,
Analizza la stringa C-string interpretando il suo contenuto come un numero integrale, che viene restituito come valore di tipo int.
atoi
domanda, ne sono consapevole. La domanda chiaramente non riguarda C, ma C ++. -1