Cómo analizar una cadena a un int en C ++?

¿Cuál es la forma en C ++ de analizar una cadena (dada como char *) en un int? El manejo de errores robusto y claro es un plus (en lugar de devolver cero ).

En el nuevo C ++ 11 hay funciones para eso: stoi, stol, stoll, stoul, etc.

int myNr = std::stoi(myString); 

Lanzará una excepción en el error de conversión.

Incluso estas nuevas funciones todavía tienen el mismo problema que Dan señaló: felizmente convertirán la cadena “11x” al número entero “11”.

Ver más: http://en.cppreference.com/w/cpp/string/basic_string/stol

Qué no hacer

Aquí está mi primer consejo: no use stringstream para esto . Si bien al principio puede parecer simple de usar, descubrirá que debe trabajar mucho más si desea robustez y un buen manejo de errores.

Aquí hay un enfoque que intuitivamente parece que debería funcionar:

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

Esto tiene un problema importante: str2int(i, "1337h4x0r") volverá a ser true y obtendré el valor 1337 . Podemos evitar este problema asegurándonos de que no haya más caracteres en el stringstream después de la conversión:

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

Solucionamos un problema, pero todavía hay un par de otros problemas.

¿Qué pasa si el número en la cadena no es la base 10? Podemos tratar de acomodar otras bases estableciendo la transmisión en el modo correcto (por ejemplo, ss << std::hex ) antes de intentar la conversión. Pero esto significa que la persona que llama debe saber a priori en qué base se encuentra el número, y ¿cómo puede saberlo la persona que llama? La persona que llama no sabe aún cuál es el número. ¡Ni siquiera saben que es un número! ¿Cómo se puede esperar que sepan de qué base se trata? Podríamos simplemente exigir que todos los números ingresados ​​a nuestros progtwigs deben ser base 10 y rechazar la entrada hexadecimal u octal como inválida. Pero eso no es muy flexible o robusto. No hay una solución simple para este problema. No puede simplemente intentar la conversión una vez para cada base, porque la conversión decimal siempre tendrá éxito para los números octales (con un cero inicial) y la conversión octal puede tener éxito para algunos números decimales. Así que ahora tienes que buscar un cero inicial. ¡Pero espera! Los números hexadecimales también pueden comenzar con un cero inicial (0x ...). Suspiro.

Incluso si tiene éxito en lidiar con los problemas anteriores, todavía hay otro problema mayor: ¿qué sucede si la persona que llama necesita distinguir entre entrada incorrecta (por ejemplo, "123foo") y un número que está fuera del rango de int (por ejemplo, "4000000000"). para 32 bits int )? Con stringstream , no hay forma de hacer esta distinción. Solo sabemos si la conversión tuvo éxito o falló. Si falla, no tenemos forma de saber por qué falló. Como puede ver, stringstream deja mucho que desear si quiere robustez y un claro manejo de errores.

Esto me lleva a mi segundo consejo: no use Boost's lexical_cast para esto . Considere lo que la documentación de lexical_cast tiene que decir:

Cuando se requiere un mayor grado de control sobre las conversiones, std :: stringstream y std :: wstringstream ofrecen una ruta más apropiada. Cuando se requieren conversiones no basadas en la transmisión, lexical_cast es la herramienta incorrecta para el trabajo y no tiene una función especial para dichos escenarios.

¿¿Qué?? Ya hemos visto que el stringstream tiene un nivel de control pobre, y sin embargo dice que se debe usar lexical_cast lugar de lexical_cast si se necesita "un mayor nivel de control". Además, debido a que lexical_cast es solo una envoltura alrededor de la stringstream , sufre los mismos problemas que la stringstream : soporte deficiente para bases de números múltiples y manejo deficiente de errores.

La mejor solucion

Afortunadamente, alguien ya ha resuelto todos los problemas anteriores. La biblioteca estándar C contiene strtol y familia que no tienen ninguno de estos problemas.

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

Bastante simple para algo que maneja todos los casos de error y también admite cualquier número base del 2 al 36. Si la base es cero (el valor predeterminado) intentará convertir desde cualquier base. O la persona que llama puede suministrar el tercer argumento y especificar que la conversión solo debe intentarse para una base particular. Es robusto y maneja todos los errores con un mínimo esfuerzo.

Otras razones para preferir strtol (y familia):

  • Exhibe un rendimiento de tiempo de ejecución mucho mejor
  • Introduce menos sobrecarga en tiempo de comstackción (los otros extraen casi 20 veces más SLOC de los encabezados)
  • Resulta en el tamaño de código más pequeño

No hay absolutamente ninguna buena razón para usar cualquier otro método.

Esta es una forma de C más segura que atoi ()

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

C ++ con stringstream de biblioteca estándar: (gracias CMS )

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

Con la biblioteca de impulso : (gracias jk )

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

Editar: corrigió la versión de cadena de caracteres para que manejara los errores. (gracias al comentario de CMS y jk sobre la publicación original)

Puede usar el lexical_cast de Boost , que lo envuelve en una interfaz más genérica. lexical_cast(Source) lanza bad_lexical_cast en caso de error.

La buena “vieja forma C” todavía funciona. Recomiendo strtol o strtoul. Entre el estado de retorno y el ‘endPtr’, puede proporcionar una buena salida de diagnóstico. También maneja bases múltiples muy bien.

Puede usar un stringstream del libraray estándar de C ++:

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

El estado de la secuencia se configurará como fallido si se encuentra un no dígito al intentar leer un número entero.

Vea las trampas de Stream para las trampas de manejo de errores y flujos en C ++.

Puedes usar stringstream’s

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

Creo que estos tres enlaces lo resumen:

las soluciones stringstream y lexical_cast son casi lo mismo que el léxico que usa stringstream.

Algunas especializaciones del uso del elenco léxico diferente enfoque ver http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp para más detalles. Los enteros y flotantes ahora están especializados para la conversión de enteros a cadenas.

Uno puede especializar lexical_cast para sus propias necesidades y hacerlo rápido. Esta sería la solución definitiva que satisfará a todas las partes, limpia y simple.

Los artículos ya mencionados muestran una comparación entre los diferentes métodos de conversión de enteros <-> cadenas. Los siguientes enfoques tienen sentido: old c-way, spirit.karma, fastformat, simple naive loop.

Lexical_cast está bien en algunos casos, por ejemplo, para int a la conversión de cadena.

No es una buena idea convertir cadenas a int usando yeso léxico, ya que es 10-40 veces más lento que atoi, dependiendo de la plataforma / comstackdor utilizado.

Boost.Spirit.Karma parece ser la biblioteca más rápida para convertir enteros en cadenas.

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

y el bucle simple básico del artículo mencionado anteriormente es una forma más rápida de convertir cadena a int, obviamente no la más segura, strtol () parece una solución más segura

 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 biblioteca de C ++ String Toolkit (StrTk) tiene la siguiente solución:

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

El InputIterator puede ser de iteradores sin signo char *, char * o std :: string, y se espera que T sea un int firmado, como por ejemplo int, int o long

Si tiene C ++ 11, las soluciones apropiadas hoy en día son las funciones de conversión de entero C ++ en : stoi , stol , stoul , stoll , stoull . Lanzan las excepciones apropiadas cuando se les da una entrada incorrecta y usan las funciones strto* rápidas y pequeñas debajo del capó.

Si está atascado con una revisión anterior de C ++, sería su portátil para imitar estas funciones en su implementación.

A partir de C ++ 17, puede usar std::from_chars desde el como se documenta aquí .

Por ejemplo:

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

Como extra, también puede manejar otras bases, como hexadecimal.

Me gusta la respuesta de Dan Molding , solo agregaré un poco de estilo C ++ a ella:

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

Funciona tanto para std :: string como const char * a través de la conversión implícita. También es útil para la conversión base, por ejemplo, todos to_int("0x7b") y to_int("0173") y to_int("01111011", 2) y to_int("0000007B", 16) y to_int("11120", 3) y to_int("3L", 34); regresaría 123.

A diferencia de std::stoi , funciona en pre-C ++ 11. También a diferencia de std::stoi , boost::lexical_cast y stringstream arroja excepciones para cadenas extrañas como "123hohoho".

NB: Esta función tolera los espacios to_int(" 123") pero no los espacios al final, es decir, to_int(" 123") devuelve 123 mientras que to_int("123 ") arroja una excepción. Asegúrese de que esto sea aceptable para su caso de uso o ajuste el código.

Dicha función podría ser parte de STL ...

Conozco tres formas de convertir String en int:

O utilice la función stoi (String to int) o simplemente vaya con Stringstream, la tercera forma de ir a la conversión individual, el código está a continuación:

1er método

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

Segundo método

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

3er método, pero no para una conversión individual

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

Me gusta la respuesta de Dan , especialmente por evitar excepciones. Para el desarrollo de sistemas integrados y otros sistemas de bajo nivel, es posible que no exista un marco de excepción adecuado.

Se agregó una marca de espacio en blanco después de una cadena válida … estas tres líneas

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

Agregó un cheque para analizar errores también.

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

Aquí está la función 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; } 

Puedes usar este método definido.

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

Y si convirtieras de cadena a entero, harías lo siguiente.

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

La salida sería 102.

Sé que esta es una pregunta anterior, pero la he encontrado muchas veces y, hasta la fecha, todavía no he encontrado una solución muy bien diseñada que tenga las siguientes características:

  • Puede convertir cualquier base (y detectar el tipo de base)
  • Detectará datos erróneos (es decir, asegúrese de que toda la cadena, menos espacio en blanco inicial / final, sea consumida por la conversión)
  • Se asegurará de que, independientemente del tipo convertido a, el rango del valor de la cadena sea aceptable.

Entonces, aquí está el mío, con una correa de prueba. Debido a que usa las funciones C strtoull / strtoll debajo del capó, siempre convierte primero al tipo más grande disponible. Luego, si no está usando el tipo más grande, realizará comprobaciones de rango adicionales para verificar que su tipo no haya pasado (debajo) de flujo. Para esto, es un poco menos eficiente que si uno elige strtol / strtoul. Sin embargo, también funciona para cortos / caracteres y, según mi leal saber y entender, no existe una función de biblioteca estándar que también lo haga.

Disfrutar; ojalá alguien lo encuentre útil.

 #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 esto:

 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 performed. 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.