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 ).
261
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.
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
fonte
size_t
não for igual ao comprimento da string, ele parou mais cedo. Ainda retornará 11 nesse caso, maspos
será 2 em vez do comprimento da string 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29O que não fazer
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:
Isso tem um grande problema:
str2int(i, "1337h4x0r")
retornará feliztrue
ei
obterá o valor1337
. Podemos solucionar esse problema, garantindo que não haja mais caracteresstringstream
após a conversão: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 bitsint
)? Comstringstream
, 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 alexical_cast
documentação tem a dizer:O que?? Já vimos que
stringstream
tem um nível de controle ruim e, no entanto, diz questringstream
deve ser usado em vez delexical_cast
se você precisar de "um nível de controle mais alto". Além disso, comolexical_cast
é apenas um invólucrostringstream
, ele sofre dos mesmos problemasstringstream
: suporte insuficiente para várias bases numéricas e tratamento inadequado de erros.A melhor solução
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.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.
fonte
strtol
ser seguro para threads. O POSIX também requer oerrno
uso de armazenamento local de encadeamento. Mesmo em sistemas não POSIX, quase todas as implementações deerrno
sistemas multithread usam armazenamento local de encadeamento. O padrão C ++ mais recente exigeerrno
que seja compatível com POSIX. O padrão C mais recente também requererrno
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 questd::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 novostd::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 ()
C ++ com biblioteca padrão stringstream : (obrigado CMS )
Com biblioteca de reforço : (obrigado jk )
Editar: corrigida a versão stringstream para que ele lida com erros. (graças ao comentário de CMS e jk na postagem original)
fonte
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.
fonte
Você pode usar o Boost's
lexical_cast
, que envolve isso em uma interface mais genérica.lexical_cast<Target>(Source)
jogabad_lexical_cast
no fracasso.fonte
Você pode usar o a string do libraray padrão C ++:
Consulte Armadilhas de fluxo para armadilhas de tratamento de erros e fluxos em C ++.
fonte
Você pode usar o stringstream
fonte
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.
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
fonte
A Biblioteca de C ++ String Toolkit (StrTk) tem a seguinte solução:
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
fonte
v = (10 * v) + digit;
transborda desnecessariamente com a entrada de string com o valor de texto deINT_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.
fonte
A partir do C ++ 17, você pode usar
std::from_chars
o<charconv>
cabeçalho conforme documentado aqui .Por exemplo:
Como bônus, ele também pode lidar com outras bases, como hexadecimal.
fonte
Eu gosto da resposta de Dan Moulding , vou adicionar um pouco de estilo C ++ a ela:
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")
eto_int("0173")
andto_int("01111011", 2)
andto_int("0000007B", 16)
andto_int("11120", 3)
andto_int("3L", 34);
retornaria 123.Ao contrário
std::stoi
, funciona no pré-C ++ 11. Também diferentestd::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 enquantoto_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 ...
fonte
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
2º Método
Terceiro método - mas não para uma conversão individual
fonte
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
Também foi adicionada uma verificação de erros de análise.
Aqui está a função completa ..
fonte
" "
.strtol()
não está especificado para definirerrno
quando nenhuma conversão ocorre. Melhor usarif (s == end) return INCONVERTIBLE;
para detectar nenhuma conversão. E entãoif (*s == '\0' || *end != '\0')
pode simplificar paraif (*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.
E se você converter de String para Inteiro, faça o seguinte.
A saída seria 102.
fonte
atoi
não parece "a maneira C ++", à luz de outras respostas como a aceitastd::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.
StringToDecimal
é o método terra do usuário; está sobrecarregado para que possa ser chamado assim:ou isto:
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:
... 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 ambosstrtoull
estrtoll
, 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 porStringToSigned
eStringToUnsigned
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!
fonte
long long
invés deintmax_t
?if (ePtr != str)
. Além disso, useisspace((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.
fonte
atoi
na pergunta, eu estou ciente disso. A questão claramente não é sobre C, mas sobre C ++. -1