usar - ¿Cómo analizar una cadena a un int en C++?
tokenizer c++ (17)
¿Cuál es la manera C ++ de analizar una cadena (dada como char *) en un int? El manejo de errores robusto y claro es una ventaja (en lugar de devolver cero ).
Qué no hacer
Aquí está mi primer consejo: no use stringstream para esto . Si bien al principio puede parecer sencillo de usar, encontrará que tiene que 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")
devolverá felizmente y obtendré el valor 1337
. Podemos solucionar 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;
}
Hemos solucionado un problema, pero todavía hay un par de otros problemas.
¿Qué pasa si el número en la cadena no es base 10? Podemos intentar acomodar otras bases configurando el flujo 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é se basa el número, y ¿cómo puede saberlo la persona que llama? La persona que llama no sabe cuál es el número todavía. ¡Ni siquiera saben que es un número! ¿Cómo se puede esperar que sepan en qué se basa? Podríamos simplemente exigir que todos los números ingresados en nuestros programas sean de base 10 y rechazar la entrada hexadecimal u octal como no válida. 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 inicial. ¡Pero espera! Los números hexadecimales también pueden comenzar con un cero inicial (0x ...). Suspiro.
Incluso si logra resolver los problemas anteriores, existe 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 no. Si falla, no tenemos forma de saber por qué falló. Como puede ver, la stringstream
deja mucho que desear si desea robustez y un manejo claro de los errores.
Esto me lleva a mi segundo consejo: no utilice el lexical_cast
de Boost para esto . Considere lo que la documentación 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. Donde se requieren conversiones no basadas en transmisión, lexical_cast es la herramienta incorrecta para el trabajo y no es un caso especial para tales escenarios.
¿¿Qué?? Ya hemos visto que el stringstream
tiene un nivel de control bajo, y sin embargo, dice que se debe usar el lexical_cast
de lexical_cast
lugar de lexical_cast
si necesita "un nivel de control más alto". Además, debido a que lexical_cast
es solo una envoltura alrededor del stringstream
, sufre los mismos problemas que el stringstream
: pobre soporte para múltiples bases de números y mal manejo de errores.
La mejor solucion
Afortunadamente, alguien ya ha resuelto todos los problemas anteriores. La biblioteca estándar de 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 base de números del 2 al 36. Si la 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 en particular. Es robusto y maneja todos los errores con un esfuerzo mínimo.
Otras razones para preferir strtol
(y familia):
- Exhibe mucho mejor rendimiento en tiempo de ejecución.
- Introduce menos sobrecarga de tiempo de compilación (los otros incorporan casi 20 veces más SLOC de los encabezados)
- Da como resultado el tamaño de código más pequeño
No hay absolutamente ninguna buena razón para usar cualquier otro método.
Conozco tres formas de convertir String en int:
Utilice la función stoi (String to int) o simplemente vaya con Stringstream, la tercera forma de realizar una conversión individual, el código está abajo:
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 <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;
}
Tercer 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 ;
}
Creo que estos tres enlaces lo resumen:
- http://tinodidriksen.com/2010/02/07/cpp-convert-int-to-string-speed/
- http://tinodidriksen.com/2010/02/16/cpp-convert-string-to-int-speed/
- http://www.fastformat.org/performance.html
Las soluciones stringstream y lexical_cast son casi las mismas que la conversión léxica que usa stringstream.
Algunas especializaciones de cast léxico utilizan un enfoque diferente, consulte http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp para obtener más información. Los enteros y los flotadores ahora están especializados para la conversión de enteros a cadenas.
Uno puede especializarse en lexical_cast para sus propias necesidades y hacerlo rápido. Esta sería la solución definitiva que satisface a todas las partes, limpia y simple.
Los artículos ya mencionados muestran la comparación entre diferentes métodos de conversión de enteros <-> cadenas. Los siguientes enfoques tienen sentido: antiguo c-way, spirit.karma, fastformat, simple naive loop.
Lexical_cast está bien en algunos casos, por ejemplo, para la conversión de cadenas a int.
No es una buena idea convertir la cadena en 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;
}
Desde C ++ 17 en adelante puede usar std::from_chars
desde el <charconv>
como se documenta here .
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.
En el nuevo C ++ 11 hay funciones para eso: stoi, stol, stoll, stoul y así sucesivamente.
int myNr = std::stoi(myString);
Se lanzará una excepción en el error de conversión.
Incluso estas nuevas funciones siguen teniendo el mismo problema como lo señaló Dan: felizmente convertirán la cadena "11x" al entero "11".
Ver más: http://en.cppreference.com/w/cpp/string/basic_string/stol
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 stringstream estándar: (gracias CMS )
int str2int (const string &str) {
stringstream ss(str);
int num;
if((ss >> num).fail())
{
//ERROR
}
return num;
}
Con boost librería boost : (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 cadena de caracteres para que maneje los errores. (Gracias al comentario de CMS y jk en la publicación original)
La biblioteca del kit de herramientas de cadenas de C ++ (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;
}
El InputIterator puede ser de caracteres sin signo char *, char * o std :: string, y se espera que T sea un int firmado, tal como int firmado, int o long
La buena ''vieja forma 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.
Me gusta la respuesta de Dan , especialmente por evitar las excepciones. Para el desarrollo de sistemas integrados y otros sistemas de desarrollo 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++;
}
Agregado un cheque para errores de análisis también.
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;
}
Me gusta share , solo le añadiré 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 para const char * a través de la conversión implícita. También es útil para la conversión de base, por ejemplo, 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);
Volvería 123.
A diferencia de std::stoi
, funciona en pre-C ++ 11. También, a diferencia de std::stoi
, boost::lexical_cast
y stringstream
, lanza excepciones para cadenas extrañas como "123hohoho".
Nota: esta función tolera espacios to_int(" 123")
pero no espacios finales, es decir, to_int(" 123")
devuelve 123 mientras que to_int("123 ")
lanza una excepción. Asegúrese de que esto sea aceptable para su caso de uso o ajuste el código.
Tal función podría ser parte de STL ...
Podrías usar este método definido.
#define toInt(x) {atoi(x.c_str())};
Y si tuviera que convertir de String a un entero, solo haría lo siguiente.
int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}
La salida sería de 102.
Puede utilizar un stringstream de la biblioteca estándar de C ++:
stringstream ss(str);
int x;
ss >> x;
if(ss) { // <-- error handling
// use x
} else {
// not a number
}
El estado del flujo se configurará para que falle si se encuentra un dígito que no es cuando se intenta leer un número entero.
Vea Corrientes de la trampa para las trampas de la gestión de errores y las corrientes en C ++.
Puedes usar stringstream''s
int str2int (const string &str) {
stringstream ss(str);
int num;
ss >> num;
return num;
}
Sé que esta es una pregunta más antigua, pero la he encontrado tantas veces y, hasta la fecha, todavía no he encontrado una solución 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, garantizará que toda la cadena, menos espacios en blanco iniciales y finales, se consuma por la conversión)
- Se asegurará de que, independientemente del tipo al que se convierte, el rango del valor de la cadena sea aceptable.
Entonces, aquí está la mía, con una correa de prueba. Debido a que utiliza 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á verificaciones de rango adicionales para verificar que su tipo no haya sobrepasado (sub). Para esto, es un poco menos eficaz que si uno eligiera correctamente strtol / strtoul. Sin embargo, también funciona para cortocircuitos / caracteres y, a mi entender, no existe una función de biblioteca estándar que haga eso, también.
Disfrutar; Esperemos que 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 este último. Esto asegura que si el tipo de ''a'' cambia uno no obtiene malos resultados. Ojalá el compilador pudiera entenderlo como:
int a; a = StringToDecimal("100");
... pero, C ++ no deduce 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 proporcionando algunas garantías adicionales (por ejemplo, la entrada negativa no está permitida si no está firmada y garantiza que toda la cadena se haya convertido).
CstrtoxllWrapper
es usado por StringToSigned
y StringToUnsigned
con el tipo más grande (long long / unsigned long long) disponible para el compilador; Esto permite realizar la conversión máxima. Entonces, si es necesario, StringToSigned
/ StringToUnsigned
realiza las comprobaciones finales se basan en el tipo subyacente. Finalmente, el método de punto final StringToDecimal
,, decide a cuál de los métodos de la plantilla StringTo * llamar según el tipo de firma subyacente.
Creo que la mayor parte de la basura puede ser optimizada por el compilador; Casi todo debe ser determinista en tiempo de compilación. ¡Cualquier comentario sobre este aspecto sería interesante para mí!
Si tiene C ++ 11, las soluciones adecuadas en la actualidad son las funciones de conversión de enteros de C ++ en <string>
: 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 se queda estancado con una revisión anterior de C ++, sería más fácil que usted imite estas funciones en su implementación.
En C, puedes usar int atoi (const char * str)
,
Analiza la cadena de caracteres C interpretando su contenido como un número integral, que se devuelve como un valor de tipo int.