¿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 )
¿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 )
Respuestas:
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 señaló Dan: felizmente convertirán la cadena "11x" al número entero "11".
Ver más: http://en.cppreference.com/w/cpp/string/basic_string/stol
size_t
no es igual a la longitud de la cadena, entonces se detuvo antes. Todavía devolverá 11 en ese caso, pero pos
será 2 en lugar de la longitud de la cadena 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29
Aquí está mi primer consejo: no use stringstream para esto . Si bien al principio puede parecer simple de usar, encontrará que debe hacer mucho trabajo adicional si desea robustez y 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")
felizmente regresará true
y i
obtendrá el valor 1337
. Podemos solucionar este problema asegurándonos de que no haya más caracteres 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 otros problemas.
¿Qué pasa si el número en la cadena no es base 10? Podemos intentar acomodar otras bases configurando 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 qué base es el número, y ¿cómo puede saberlo? La persona que llama aún no sabe cuál es el número. Ni siquiera saben que es¡un número! ¿Cómo se puede esperar que sepan qué base es? Podríamos exigir que todos los números ingresados a nuestros programas deben ser de base 10 y rechazar el ingreso hexadecimal u octal como no válido. Pero eso no es muy flexible ni 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 a la izquierda. ¡Pero espera! Los números hexadecimales también pueden comenzar con un cero inicial (0x ...). Suspiro.
Incluso si logra resolver los problemas anteriores, todavía hay otro problema mayor: ¿qué sucede si la persona que llama necesita distinguir entre una 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 desea robustez y manejo claro de errores.
Esto me lleva a mi segundo consejo: no use Boost lexical_cast
para esto . Considere lo que la lexical_cast
documentación 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 secuencias, lexical_cast es la herramienta incorrecta para el trabajo y no está especialmente diseñada para tales escenarios.
¿¿Qué?? Ya hemos visto que stringstream
tiene un bajo nivel de control y, sin embargo, dice que stringstream
debe usarse en lugar de lexical_cast
si necesita "un mayor nivel de control". Además, debido a que lexical_cast
es solo una envoltura stringstream
, sufre los mismos problemas que stringstream
tiene: pobre soporte para múltiples bases de números y pobre manejo de errores.
Afortunadamente, alguien ya ha resuelto todos los problemas anteriores. La biblioteca estándar de C contiene una strtol
familia que no tiene 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 base de números del 2 al 36. Si base
es cero (el valor predeterminado) intentará convertir desde cualquier base. O la persona que llama puede proporcionar 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):
No hay absolutamente ninguna buena razón para usar ningún otro método.
strtol
ser seguro para subprocesos. POSIX también requiere errno
usar almacenamiento local de subprocesos. Incluso en sistemas que no son POSIX, casi todas las implementaciones de errno
sistemas multiproceso utilizan almacenamiento local de subprocesos. El último estándar C ++ requiere errno
que sea compatible con POSIX. El último estándar C también requiere errno
tener almacenamiento local de subprocesos. Incluso en Windows, que definitivamente no es compatible con POSIX, errno
es seguro para subprocesos y, por extensión, también lo es strtol
.
std::stol
para esto, que arrojará excepciones en lugar de devolver constantes.
std::stol
incluso antes de agregarla al lenguaje C ++. Dicho esto, no creo que sea justo decir que esto es "codificación C dentro de C ++". Es tonto decir que std::strtol
es codificación C cuando es explícitamente parte del lenguaje C ++. Mi respuesta se aplicó perfectamente a C ++ cuando se escribió y aún se aplica incluso con la nueva std::stol
. Llamar a funciones que pueden generar excepciones no siempre es lo mejor para cada situación de programación.
Esta es una forma C más segura que atoi ()
const char* str = "123";
int i;
if(sscanf(str, "%d", &i) == EOF )
{
/* error */
}
C ++ con secuencia de cadena 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 <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
}
Editar: se corrigió la versión de secuencia de cadena para que maneje los errores. (gracias al comentario de CMS y jk en la publicación original)
La buena y antigua forma de C todavía funciona. Recomiendo strtol o strtoul. Entre el estado de retorno y el 'endPtr', puede dar una buena salida de diagnóstico. También maneja múltiples bases muy bien.
Puedes usar Boost'slexical_cast
, que envuelve esto en una interfaz más genérica.
lexical_cast<Target>(Source)
tira bad_lexical_cast
al fracaso.
Puede usar un flujo de cadena 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 establecerá en error 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 streams 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 lexical cast está usando stringstream.
Algunas especializaciones del reparto léxico utilizan un enfoque diferente; consulte http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp para obtener más detalles. Los enteros y flotantes ahora están especializados para la conversión de entero a cadena.
Uno puede especializarse en lexical_cast para sus propias necesidades y hacerlo rápido. Esta sería la solución definitiva para 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: viejo c-way, spirit.karma, fastformat, simple ingenuo loop.
Lexical_cast está bien en algunos casos, por ejemplo, para la conversión de int a string.
No es una buena idea convertir una cadena a int usando el reparto léxico, ya que es 10-40 veces más lento que atoi, dependiendo de la plataforma / compilador 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 cadenas a int, obviamente no es 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<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 puede ser de iteradores char *, char * o std :: string sin firmar, y se espera que T sea un int con signo, como con signo int, int o long
v = (10 * v) + digit;
se desborda innecesariamente con la entrada de cadena con el valor de texto de INT_MIN
. La tabla tiene un valor cuestionable frente a simplementedigit >= '0' && digit <= '9'
Si usted tiene 11 C ++, las soluciones adecuadas hoy en día son el C ++ número entero funciones de conversión en <string>
: stoi
, stol
, stoul
, stoll
, stoull
. Lanzan excepciones apropiadas cuando se les da una entrada incorrecta y usan el rápido y pequeñostrto*
funciones debajo del capó.
Si está atascado con una revisión anterior de C ++, sería portátil para usted imitar estas funciones en su implementación.
Desde C ++ 17 en adelante, puede usar std::from_chars
desde el <charconv>
encabezado como se documenta aquí .
Por ejemplo:
#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;
}
}
Como beneficio adicional, también puede manejar otras bases, como hexadecimal.
Me gusta la respuesta de Dan Moulding , solo le agregaré un poco de estilo 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;
}
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 de 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 yto_int("3L", 34);
devolvería 123.
A diferencia de std::stoi
esto funciona en pre-C ++ 11. También a diferencia std::stoi
, boost::lexical_cast
ystringstream
arroja excepciones para cadenas extrañas como "123hohoho".
NB: esta función tolera los espacios iniciales pero no los espacios finales, es decir, to_int(" 123")
devuelve 123 mientras to_int("123 ")
lanza 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:
Use la función stoi (String to int) o simplemente vaya con Stringstream, la tercera forma de 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';
2do método
#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;
}
3.er 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 <<in-48 ;
}
Me gusta la respuesta de Dan , especialmente por evitar excepciones. Para el desarrollo de sistemas integrados y otros desarrollos de sistemas de bajo nivel, es posible que no haya un marco de excepción adecuado disponible.
Se agregó una marca de espacio en blanco después de una cadena válida ... estas tres líneas
while (isspace(*end)) {
end++;
}
También se agregó una comprobación de errores de análisis.
if ((errno != 0) || (s == end)) {
return INCONVERTIBLE;
}
Aquí está la función 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()
no se especifica para establecer errno
cuando no se produce conversión. Mejor usar if (s == end) return INCONVERTIBLE;
para detectar que no hay conversión. Y luego if (*s == '\0' || *end != '\0')
puede simplificarse a if (*end)
2) || l > LONG_MAX
y || l < LONG_MIN
no sirve para nada: nunca son ciertas.
Podrías usar este método definido.
#define toInt(x) {atoi(x.c_str())};
Y si fuera a convertir de String a Integer, simplemente haría lo siguiente.
int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}
El resultado sería 102.
atoi
no parece "la forma C ++", a la luz de otras respuestas como la aceptada std::stoi()
.
Sé que esta es una pregunta anterior, pero me la he encontrado muchas veces y, hasta la fecha, todavía no he encontrado una solución bien diseñada que tenga las siguientes características:
Entonces, aquí está el mío, con una correa de prueba. Debido a que usa las funciones C strtoull / strtoll debajo del capó, siempre se convierte primero al tipo más grande disponible. Luego, si no está utilizando el tipo más grande, realizará comprobaciones de rango adicionales para verificar que su tipo no se haya desbordado. Para esto, es un poco menos eficaz que si uno eligiera correctamente strtol / strtoul. Sin embargo, también funciona para cortos / caracteres y, que yo sepa, no existe una función de biblioteca estándar que haga eso también.
Disfrutar; ojalá alguien lo encuentre útil.
#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
es el método de usuario-tierra; está sobrecargado, por lo que puede llamarse así:
int a; a = StringToDecimal<int>("100");
o esto:
int a; StringToDecimal(a, "100");
Odio repetir el tipo int, así que prefiero el último. Esto asegura que si el tipo de 'a' cambia, uno no obtendrá malos resultados. Desearía que el compilador pudiera resolverlo como:
int a; a = StringToDecimal("100");
... pero, C ++ no deduce los tipos de retorno de plantilla, así que eso es lo mejor que puedo obtener.
La implementación es bastante simple:
CstrtoxllWrapper
envuelve ambos strtoull
y strtoll
, llamando lo que sea necesario en función de la firma del tipo de plantilla y brindando algunas garantías adicionales (por ejemplo, la entrada negativa no se permite si no está firmada y asegura que se haya convertido toda la cadena).
CstrtoxllWrapper
es utilizado por StringToSigned
y StringToUnsigned
con el tipo más grande (largo largo / sin signo largo largo) disponible para el compilador; Esto permite que se realice la conversión máxima. Luego, si es necesario, StringToSigned
/ StringToUnsigned
realiza las comprobaciones de rango final en el tipo subyacente. Finalmente, el método del punto final,StringToDecimal
, decide a cuál de los métodos de plantilla StringTo * llamar en función de la firma del tipo subyacente.
Creo que la mayoría de la basura puede ser optimizada por el compilador; casi todo debería ser determinista en tiempo de compilación. ¡Cualquier comentario sobre este aspecto sería interesante para mí!
long long
lugar de intmax_t
?
if (ePtr != str)
. Además, use isspace((unsigned char) *ePtr)
para manejar adecuadamente los valores negativos de *ePtr
.
En C, puedes usar int atoi (const char * str)
,
Analiza la cadena C-string interpretando su contenido como un número integral, que se devuelve como un valor de tipo int.
atoi
la pregunta, soy consciente de ello. La pregunta claramente no es sobre C, sino sobre C ++. -1