Qual é a maneira C ++ de analisar uma string (fornecida como char *) em um int? A manipulação de erros clara e robusta é uma vantagem (em vez de retornar zero ).
Qual é a maneira C ++ de analisar uma string (fornecida como char *) em um int? A manipulação de erros clara e robusta é uma vantagem (em vez de retornar zero ).
Respostas:
No novo C ++ 11, existem funções para isso: stoi, stol, stoll, stoul e assim por diante.
int myNr = std::stoi(myString);
Irá lançar uma exceção no erro de conversão.
Mesmo essas novas funções ainda têm o mesmo problema observado por Dan: elas convertem alegremente a string "11x" para o número inteiro "11".
Veja mais: http://en.cppreference.com/w/cpp/string/basic_string/stol
size_t
não for igual ao comprimento da string, ele parou mais cedo. Ainda retornará 11 nesse caso, mas pos
será 2 em vez do comprimento da string 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29
Aqui está o meu primeiro conselho: não use stringstream para isso . Embora a princípio pareça simples de usar, você descobrirá que precisa fazer muito trabalho extra se quiser robustez e bom tratamento de erros.
Aqui está uma abordagem que parece intuitivamente que deve funcionar:
bool str2int (int &i, char const *s)
{
std::stringstream ss(s);
ss >> i;
if (ss.fail()) {
// not an integer
return false;
}
return true;
}
Isso tem um grande problema: str2int(i, "1337h4x0r")
retornará feliz true
e i
obterá o valor 1337
. Podemos solucionar esse problema, garantindo que não haja mais caracteres stringstream
após a conversão:
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;
}
Corrigimos um problema, mas ainda existem alguns outros problemas.
E se o número na sequência não for a base 10? Podemos tentar acomodar outras bases configurando o fluxo no modo correto (por exemplo ss << std::hex
) antes de tentar a conversão. Mas isso significa que o chamador deve saber a priori qual é o número base - e como o chamador pode saber disso? O chamador ainda não sabe qual é o número. Eles nem sabem que éum número! Como se espera que eles saibam qual é a base? Poderíamos apenas exigir que todos os números inseridos em nossos programas sejam da base 10 e rejeitem a entrada hexadecimal ou octal como inválida. Mas isso não é muito flexível ou robusto. Não há uma solução simples para esse problema. Você não pode simplesmente tentar a conversão uma vez para cada base, porque a conversão decimal sempre será bem-sucedida para números octais (com um zero à esquerda) e a conversão octal poderá ser bem-sucedida para alguns números decimais. Então agora você precisa verificar o zero inicial. Mas espere! Os números hexadecimais também podem começar com um zero à esquerda (0x ...). Suspiro.
Mesmo se você conseguir lidar com os problemas acima, ainda há outro problema maior: e se o chamador precisar distinguir entre entrada incorreta (por exemplo, "123foo") e um número que esteja fora do intervalo int
(por exemplo, "4000000000" para 32 bits int
)? Com stringstream
, não há como fazer essa distinção. Só sabemos se a conversão foi bem-sucedida ou falhou. Se falhar, não temos como saber por que falhou. Como você pode ver, stringstream
deixa muito a desejar se você deseja robustez e tratamento claro de erros.
Isso me leva ao meu segundo conselho: não use o Boost's lexical_cast
para isso . Considere o que a lexical_cast
documentação tem a dizer:
Onde um nível mais alto de controle é necessário sobre conversões, std :: stringstream e std :: wstringstream oferecem um caminho mais apropriado. Nos casos em que são necessárias conversões não baseadas em fluxo, lexical_cast é a ferramenta errada para o trabalho e não é especificada para esses cenários.
O que?? Já vimos que stringstream
tem um nível de controle ruim e, no entanto, diz que stringstream
deve ser usado em vez de lexical_cast
se você precisar de "um nível de controle mais alto". Além disso, como lexical_cast
é apenas um invólucro stringstream
, ele sofre dos mesmos problemas stringstream
: suporte insuficiente para várias bases numéricas e tratamento inadequado de erros.
Felizmente, alguém já resolveu todos os problemas acima. A biblioteca padrão C contém uma strtol
família que não apresenta nenhum desses 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;
}
Muito simples para algo que lida com todos os casos de erro e também suporta qualquer base numérica de 2 a 36. Se base
for zero (o padrão), ele tentará converter de qualquer base. Ou o chamador pode fornecer o terceiro argumento e especificar que a conversão deve ser tentada apenas para uma base específica. É robusto e lida com todos os erros com um esforço mínimo.
Outras razões para preferir strtol
(e família):
Não há absolutamente nenhuma boa razão para usar qualquer outro método.
strtol
ser seguro para threads. O POSIX também requer o errno
uso de armazenamento local de encadeamento. Mesmo em sistemas não POSIX, quase todas as implementações de errno
sistemas multithread usam armazenamento local de encadeamento. O padrão C ++ mais recente exige errno
que seja compatível com POSIX. O padrão C mais recente também requer errno
armazenamento local de encadeamento. Mesmo no Windows, que definitivamente não é compatível com POSIX, errno
é seguro para threads e, por extensão, também é strtol
.
std::stol
para isso, que lançará adequadamente exceções em vez de retornar constantes.
std::stol
mesmo foi adicionada à linguagem C ++. Dito isto, não acho justo dizer que isso é "codificação C dentro de C ++". É bobagem dizer que std::strtol
é a codificação C quando faz parte explicitamente da linguagem C ++. Minha resposta se aplicava perfeitamente ao C ++ quando ele foi escrito e ainda se aplica mesmo ao novo std::stol
. Chamar funções que podem gerar exceções nem sempre é o melhor para todas as situações de programação.
Esta é uma maneira C mais segura do que atoi ()
const char* str = "123";
int i;
if(sscanf(str, "%d", &i) == EOF )
{
/* error */
}
C ++ com biblioteca padrão stringstream : (obrigado CMS )
int str2int (const string &str) {
stringstream ss(str);
int num;
if((ss >> num).fail())
{
//ERROR
}
return num;
}
Com biblioteca de reforço : (obrigado 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: corrigida a versão stringstream para que ele lida com erros. (graças ao comentário de CMS e jk na postagem original)
O bom e velho jeito C ainda funciona. Eu recomendo strtol ou strtoul. Entre o status de retorno e o 'endPtr', você pode fornecer uma boa saída de diagnóstico. Ele também lida com várias bases bem.
Você pode usar o Boost'slexical_cast
, que envolve isso em uma interface mais genérica.
lexical_cast<Target>(Source)
joga bad_lexical_cast
no fracasso.
Você pode usar o a string do libraray padrão C ++:
stringstream ss(str);
int x;
ss >> x;
if(ss) { // <-- error handling
// use x
} else {
// not a number
}
O estado do fluxo será configurado para falhar se um não dígito for encontrado ao tentar ler um número inteiro.
Consulte Armadilhas de fluxo para armadilhas de tratamento de erros e fluxos em C ++.
Você pode usar o stringstream
int str2int (const string &str) {
stringstream ss(str);
int num;
ss >> num;
return num;
}
Eu acho que esses três links resumem:
As soluções stringstream e lexical_cast são praticamente as mesmas que o elenco lexical está usando o stringstream.
Algumas especializações de elenco lexical usam abordagens diferentes, consulte http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp para obter detalhes. Agora, números inteiros e flutuantes são especializados para conversão de número inteiro em cadeia.
Pode-se especializar o lexical_cast para suas próprias necessidades e torná-lo rápido. Esta seria a solução definitiva para todas as partes, limpa e simples.
Os artigos já mencionados mostram comparação entre os diferentes métodos de conversão de cadeias inteiras <->. As abordagens a seguir fazem sentido: c-way antigo, spirit.karma, fastformat, loop simples e ingênuo.
Lexical_cast está bom em alguns casos, por exemplo, na conversão de int para string.
A conversão de string para int usando conversão lexical não é uma boa ideia, pois é 10-40 vezes mais lenta que atoi, dependendo da plataforma / compilador usada.
O Boost.Spirit.Karma parece ser a biblioteca mais rápida para converter números inteiros em string.
ex.: generate(ptr_char, int_, integer_number);
e o loop simples básico do artigo mencionado acima é a maneira mais rápida de converter uma string para int, obviamente não a mais segura, strtol () parece uma solução mais 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;
}
A Biblioteca de C ++ String Toolkit (StrTk) tem a seguinte solução:
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;
}
O InputIterator pode ser de iteradores char *, char * ou std :: string não assinados, e espera-se que T seja um int assinado, como int, int ou long
v = (10 * v) + digit;
transborda desnecessariamente com a entrada de string com o valor de texto de INT_MIN
. Tabela é de valor questionável vs simplesmentedigit >= '0' && digit <= '9'
Se você tiver C ++ 11, as soluções adequadas nos dias de hoje são o C ++ integer funções de conversão em <string>
: stoi
, stol
, stoul
, stoll
, stoull
. Eles lançam exceções apropriadas quando recebem informações incorretas e usam os métodos rápido e pequenostrto*
funções sob o capô.
Se você está preso a uma revisão anterior do C ++, seria portátil da sua parte imitar essas funções em sua implementação.
A partir do C ++ 17, você pode usar std::from_chars
o <charconv>
cabeçalho conforme documentado aqui .
Por exemplo:
#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 bônus, ele também pode lidar com outras bases, como hexadecimal.
Eu gosto da resposta de Dan Moulding , vou adicionar um pouco de estilo C ++ a ela:
#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;
}
Ele funciona para std :: string e const char * através da conversão implícita. Também é útil para conversão de base, por exemplo, all to_int("0x7b")
e to_int("0173")
and to_int("01111011", 2)
and to_int("0000007B", 16)
and to_int("11120", 3)
andto_int("3L", 34);
retornaria 123.
Ao contrário std::stoi
, funciona no pré-C ++ 11. Também diferente std::stoi
, boost::lexical_cast
estringstream
lança exceções para strings estranhos como "123hohoho".
Nota: Esta função tolera espaços à esquerda, mas não espaços à direita, ou seja, to_int(" 123")
retorna 123 enquanto to_int("123 ")
lança exceção. Verifique se isso é aceitável para o seu caso de uso ou ajuste o código.
Essa função pode fazer parte do STL ...
Conheço três maneiras de converter String em int:
Use a função stoi (String para int) ou apenas siga a Stringstream, a terceira maneira de conversão individual; o código está abaixo:
1º 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';
2º 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;
}
Terceiro método - mas não para uma conversão 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 ;
}
Eu gosto da resposta do Dan , especialmente por evitar exceções. Para o desenvolvimento de sistemas embarcados e outro desenvolvimento de sistema de baixo nível, pode não haver uma estrutura de exceção adequada disponível.
Adicionada uma verificação de espaço em branco após uma sequência válida ... essas três linhas
while (isspace(*end)) {
end++;
}
Também foi adicionada uma verificação de erros de análise.
if ((errno != 0) || (s == end)) {
return INCONVERTIBLE;
}
Aqui está a função 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()
não está especificado para definir errno
quando nenhuma conversão ocorre. Melhor usar if (s == end) return INCONVERTIBLE;
para detectar nenhuma conversão. E então if (*s == '\0' || *end != '\0')
pode simplificar para if (*end)
2) || l > LONG_MAX
e || l < LONG_MIN
não serve para nada - eles nunca são verdadeiros.
Você poderia usar este método definido.
#define toInt(x) {atoi(x.c_str())};
E se você converter de String para Inteiro, faça o seguinte.
int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}
A saída seria 102.
atoi
não parece "a maneira C ++", à luz de outras respostas como a aceita std::stoi()
.
Sei que essa é uma pergunta mais antiga, mas já a encontrei muitas vezes e, até o momento, ainda não encontrei uma solução bem modelada com as seguintes características:
Então, aqui está o meu, com uma tira de teste. Como ele usa as funções C strtoull / strtoll sob o capô, ele sempre converte primeiro no maior tipo disponível. Então, se você não estiver usando o tipo maior, ele executará verificações de intervalo adicionais para verificar se o seu tipo não excedeu (sub) o fluxo. Para isso, é um pouco menos eficiente do que se alguém escolhesse strtol / strtoul corretamente. No entanto, ele também funciona para shorts / chars e, pelo que sei, não existe uma função de biblioteca padrão que faça isso também.
Aproveitar; espero que alguém ache ú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
é o método terra do usuário; está sobrecarregado para que possa ser chamado assim:
int a; a = StringToDecimal<int>("100");
ou isto:
int a; StringToDecimal(a, "100");
Eu odeio repetir o tipo int, então prefiro o último. Isso garante que, se o tipo de 'a' for alterado, não haverá resultados ruins. Eu gostaria que o compilador pudesse descobrir como:
int a; a = StringToDecimal("100");
... mas o C ++ não deduz os tipos de retorno de modelo, então é o melhor que posso obter.
A implementação é bem simples:
CstrtoxllWrapper
envolve ambos strtoull
e strtoll
, chamando o que for necessário, com base na assinatura do tipo de modelo e fornecendo algumas garantias adicionais (por exemplo, entrada negativa não será permitida se não estiver assinada e garante que toda a cadeia seja convertida).
CstrtoxllWrapper
é usado por StringToSigned
e StringToUnsigned
com o maior tipo (long long / unsigned long long) disponível para o compilador; isso permite que a conversão máxima seja realizada. Então, se for necessário, StringToSigned
/ StringToUnsigned
executa as verificações finais do intervalo no tipo subjacente. Finalmente, o método do ponto final,StringToDecimal
, decide qual dos métodos de modelo StringTo * chamar com base na assinatura do tipo subjacente.
Eu acho que a maior parte do lixo pode ser otimizada pelo compilador; praticamente tudo deve ser determinístico em tempo de compilação. Qualquer comentário sobre esse aspecto seria interessante para mim!
long long
invés de intmax_t
?
if (ePtr != str)
. Além disso, use isspace((unsigned char) *ePtr)
para manipular adequadamente valores negativos de *ePtr
.
Em C, você pode usar int atoi (const char * str)
,
Analisa a cadeia de caracteres C interpretando seu conteúdo como um número inteiro, retornado como um valor do tipo int.
atoi
na pergunta, eu estou ciente disso. A questão claramente não é sobre C, mas sobre C ++. -1