Como converter uma string para inteiro em C?

260

Estou tentando descobrir se existe uma maneira alternativa de converter seqüência de caracteres para número inteiro em c.

Costumo padronizar o seguinte no meu código.

char s[] = "45";

int num = atoi(s);

Então, existe uma maneira melhor ou outra?

user618677
fonte
21
Suas tags e título dizem que você deseja uma solução em C, mas sua pergunta é C ou C ++. Qual deles você quer?
In silico
1
@ Yann, desculpe por essa confusão. Eu vou preferir c.
user618677
1
Funciona, mas não é o caminho recomendado, porque não há como lidar com erros. Nunca use isso no código de produção, a menos que você possa confiar 100% na entrada.
Uwe Geuder
1
Defina 'melhor' e indique claramente por que você precisa de outra maneira.
Marquês de Lorne
3
@EJP Apenas para me aperfeiçoar.
precisa saber é o seguinte

Respostas:

185

Existe o strtolque é melhor IMO. Também gostei strtonum, então use-o se você o tiver (mas lembre-se de que não é portátil):

long long
     strtonum(const char *nptr, long long minval, long long maxval,
     const char **errstr);

EDITAR

Você também pode estar interessado strtoumaxestrtoimax quais são as funções padrão no C99. Por exemplo, você poderia dizer:

uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
    /* Could not convert. */

De qualquer forma, fique longe de atoi:

A chamada atoi (str) deve ser equivalente a:

(int) strtol(str, (char **)NULL, 10)

exceto que o tratamento de erros pode ser diferente. Se o valor não puder ser representado, o comportamento será indefinido .

cnicutar
fonte
o que eu preciso incluir strtonum? Eu continuo recebendo um aviso de declaração implícita #
30413
@ trideceth12 Nos sistemas em que está disponível, deve ser declarado em #<stdlib.h>. No entanto, você pode usar a strtoumaxalternativa padrão .
cnicutar
4
Essa resposta não parece mais curta que o primeiro código do questionador.
Azurespot
11
@NoniA. A concisão é sempre boa, mas não à custa da correção.
Cnicutar 03/09/2014
6
Não tanto errado quanto inseguro. atoi () funciona se a entrada for válida. Mas e se você fizer atoi ("gato")? strtol () definiu comportamento se o valor não puder ser representado como longo, atoi () não.
Daniel B.
27

strtolSolução robusta baseada em C89

Com:

  • nenhum comportamento indefinido (como poderia ser tido com a atoifamília)
  • uma definição mais rígida de número inteiro que strtol(por exemplo, nenhum espaço em branco à esquerda nem caracteres de lixeira à direita)
  • classificação do caso de erro (por exemplo, para fornecer mensagens de erro úteis aos usuários)
  • um "testinguite"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

typedef enum {
    STR2INT_SUCCESS,
    STR2INT_OVERFLOW,
    STR2INT_UNDERFLOW,
    STR2INT_INCONVERTIBLE
} str2int_errno;

/* Convert string s to int out.
 *
 * @param[out] out The converted int. Cannot be NULL.
 *
 * @param[in] s Input string to be converted.
 *
 *     The format is the same as strtol,
 *     except that the following are inconvertible:
 *
 *     - empty string
 *     - leading whitespace
 *     - any trailing characters that are not part of the number
 *
 *     Cannot be NULL.
 *
 * @param[in] base Base to interpret string in. Same range as strtol (2 to 36).
 *
 * @return Indicates if the operation succeeded, or why it failed.
 */
str2int_errno str2int(int *out, char *s, int base) {
    char *end;
    if (s[0] == '\0' || isspace(s[0]))
        return STR2INT_INCONVERTIBLE;
    errno = 0;
    long l = strtol(s, &end, base);
    /* Both checks are needed because INT_MAX == LONG_MAX is possible. */
    if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
        return STR2INT_OVERFLOW;
    if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN))
        return STR2INT_UNDERFLOW;
    if (*end != '\0')
        return STR2INT_INCONVERTIBLE;
    *out = l;
    return STR2INT_SUCCESS;
}

int main(void) {
    int i;
    /* Lazy to calculate this size properly. */
    char s[256];

    /* Simple case. */
    assert(str2int(&i, "11", 10) == STR2INT_SUCCESS);
    assert(i == 11);

    /* Negative number . */
    assert(str2int(&i, "-11", 10) == STR2INT_SUCCESS);
    assert(i == -11);

    /* Different base. */
    assert(str2int(&i, "11", 16) == STR2INT_SUCCESS);
    assert(i == 17);

    /* 0 */
    assert(str2int(&i, "0", 10) == STR2INT_SUCCESS);
    assert(i == 0);

    /* INT_MAX. */
    sprintf(s, "%d", INT_MAX);
    assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
    assert(i == INT_MAX);

    /* INT_MIN. */
    sprintf(s, "%d", INT_MIN);
    assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
    assert(i == INT_MIN);

    /* Leading and trailing space. */
    assert(str2int(&i, " 1", 10) == STR2INT_INCONVERTIBLE);
    assert(str2int(&i, "1 ", 10) == STR2INT_INCONVERTIBLE);

    /* Trash characters. */
    assert(str2int(&i, "a10", 10) == STR2INT_INCONVERTIBLE);
    assert(str2int(&i, "10a", 10) == STR2INT_INCONVERTIBLE);

    /* int overflow.
     *
     * `if` needed to avoid undefined behaviour
     * on `INT_MAX + 1` if INT_MAX == LONG_MAX.
     */
    if (INT_MAX < LONG_MAX) {
        sprintf(s, "%ld", (long int)INT_MAX + 1L);
        assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
    }

    /* int underflow */
    if (LONG_MIN < INT_MIN) {
        sprintf(s, "%ld", (long int)INT_MIN - 1L);
        assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
    }

    /* long overflow */
    sprintf(s, "%ld0", LONG_MAX);
    assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);

    /* long underflow */
    sprintf(s, "%ld0", LONG_MIN);
    assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);

    return EXIT_SUCCESS;
}

GitHub upstream .

Baseado em: https://stackoverflow.com/a/6154614/895245

Ciro Santilli adicionou uma nova foto
fonte
3
Nice robust str2int(). Pedante: uso isspace((unsigned char) s[0]).
chux - Restabelece Monica
@chux thanks! Você pode explicar um pouco mais por que o (unsigned char)elenco pode fazer a diferença?
Ciro Santilli publicou
O compilador IAR C avisa isso l > INT_MAXe l < INT_MINfaz uma comparação inteira sem sentido, pois qualquer um dos resultados é sempre falso. O que acontece se eu os alterar l >= INT_MAXe l <= INT_MINapagar os avisos? No ARM C, long e int são tipos de dados básicos
ecle
@ alteração do código para gerar l >= INT_MAXfuncionalidade incorreta: Exemplo retornando STR2INT_OVERFLOWcom entrada "32767"e 16 bits int. Use uma compilação condicional. Exemplo .
chux - Restabelece Monica
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;seria melhor como if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}para permitir que o código de chamada para usar errnoem intfora-de-gama. O mesmo para if (l < INT_MIN....
chux - Restabelece Monica
24

Não use funções do ato...grupo. Estes estão quebrados e praticamente inúteis. Uma solução moderadamente melhor seria usar sscanf, embora também não seja perfeita.

Para converter string em número inteiro, funções do strto...grupo devem ser usadas. No seu caso específico, seria strtolfunção.

Formiga
fonte
7
sscanfna verdade, possui um comportamento indefinido se tentar converter um número fora do intervalo de seu tipo (por exemplo, sscanf("999999999999999999999", "%d", &n)).
21411 Keith Thompson
1
@ Keith Thompson: É exatamente isso que eu quero dizer. atoinão fornece feedback significativo de sucesso / falha e possui um comportamento indefinido no estouro. sscanffornece feedback de êxito / falha das sortes (o valor de retorno, que é o que o torna "moderadamente melhor"), mas ainda tem um comportamento indefinido no estouro. Somente strtolé uma solução viável.
AnT
1
Acordado; Eu só queria enfatizar o problema potencialmente fatal sscanf. (Embora eu confesso que às vezes usam atoi, geralmente para os programas que eu não esperava sobreviver mais de 10 minutos antes de apagar a fonte.)
Keith Thompson
5

Você pode codificar um pouco de atoi () por diversão:

int my_getnbr(char *str)
{
  int result;
  int puiss;

  result = 0;
  puiss = 1;
  while (('-' == (*str)) || ((*str) == '+'))
  {
      if (*str == '-')
        puiss = puiss * -1;
      str++;
  }
  while ((*str >= '0') && (*str <= '9'))
  {
      result = (result * 10) + ((*str) - '0');
      str++;
  }
  return (result * puiss);
}

Você também pode torná-lo recursivo, que pode ser antigo em 3 linhas =)

jDourlens
fonte
Muito obrigado .. Mas você poderia me dizer como o código abaixo funciona? code((str *) - '0')code
user618677
um personagem tem um valor ascii. Se você é do tipo linux: man ascii no shell ou, se não, vá para: table-ascii.com . Você verá que o caractere '0' = 68 (eu acho) para um int. Então, para obter o número de '9' (é '0' + 9), você obtém 9 = '9' - '0'. Você entendeu?
jDourlens
1
1) O código permite "----1" 2) Tem comportamento indefinido com intestouro quando o resultado deve ser INT_MIN. Consideremy_getnbr("-2147483648")
chux - Reinstate Monica
Obrigado pela precisão, foi apenas por mostrar um pequeno exemplo. Como é dito por diversão e aprendizado. Você deve usar definitivamente a lib standart para esse tipo de tarefa. Mais rápido e seguro!
JDourlens
2

Só queria compartilhar uma solução por muito tempo sem assinatura.

unsigned long ToUInt(char* str)
{
    unsigned long mult = 1;
    unsigned long re = 0;
    int len = strlen(str);
    for(int i = len -1 ; i >= 0 ; i--)
    {
        re = re + ((int)str[i] -48)*mult;
        mult = mult*10;
    }
    return re;
}
Jacob
fonte
1
Não lida com excesso. Além disso, o parâmetro deve ser const char *.
precisa saber é o seguinte
2
Além disso, o que isso 48significa? Você está assumindo que esse é o valor de '0'onde o código será executado? Por favor, não imponha suposições tão amplas ao mundo!
precisa
@TobySpeight Sim, suponho que 48 represente '0' na tabela ascii.
Jacob
3
Nem todo o mundo é ASCII - use '0'como deveria.
Toby Speight
é recomendável usar a função strtoul .
rapidclock
1
int atoi(const char* str){
    int num = 0;
    int i = 0;
    bool isNegetive = false;
    if(str[i] == '-'){
        isNegetive = true;
        i++;
    }
    while (str[i] && (str[i] >= '0' && str[i] <= '9')){
        num = num * 10 + (str[i] - '0');
        i++;
    }
    if(isNegetive) num = -1 * num;
    return num;
}
Biswajit Karmakar
fonte
-1

Você sempre pode rolar o seu próprio!

#include <stdio.h>
#include <string.h>
#include <math.h>

int my_atoi(const char* snum)
{
    int idx, strIdx = 0, accum = 0, numIsNeg = 0;
    const unsigned int NUMLEN = (int)strlen(snum);

    /* Check if negative number and flag it. */
    if(snum[0] == 0x2d)
        numIsNeg = 1;

    for(idx = NUMLEN - 1; idx >= 0; idx--)
    {
        /* Only process numbers from 0 through 9. */
        if(snum[strIdx] >= 0x30 && snum[strIdx] <= 0x39)
            accum += (snum[strIdx] - 0x30) * pow(10, idx);

        strIdx++;
    }

    /* Check flag to see if originally passed -ve number and convert result if so. */
    if(!numIsNeg)
        return accum;
    else
        return accum * -1;
}

int main()
{
    /* Tests... */
    printf("Returned number is: %d\n", my_atoi("34574"));
    printf("Returned number is: %d\n", my_atoi("-23"));

    return 0;
}

Isso fará o que você deseja sem confusão.

ButchDean
fonte
2
Mas por que? Isso não verifica o estouro e simplesmente ignora os valores do lixo. Não há razão para não usar a strto...família de funções. Eles são portáteis e significativamente melhores.
chad
1
Estranho de usar em 0x2d, 0x30vez de '-', '0'. Não permite '+'sinal. Por que (int)lançar (int)strlen(snum)? UB se a entrada for "". UB quando o resultado é INT_MINdevido ao intestouro comaccum += (snum[strIdx] - 0x30) * pow(10, idx);
chux - Restabelece Monica
@chux - Este código é um código de demonstração. Existem correções fáceis para o que você descreveu como possíveis problemas.
ButchDean
2
@ButchDean O que você descreve como "código de demonstração" será usado por outras pessoas que não têm idéia de todos os detalhes. Somente a pontuação negativa e os comentários nesta resposta os protegem agora. Na minha opinião, o "código de demonstração" deve ter uma qualidade muito maior.
Roland Illig
@RolandIllig Em vez de ser crítico, não seria mais útil para outras pessoas colocar sua própria solução?
precisa saber é o seguinte
-1

Esta função irá ajudá-lo

int strtoint_n(char* str, int n)
{
    int sign = 1;
    int place = 1;
    int ret = 0;

    int i;
    for (i = n-1; i >= 0; i--, place *= 10)
    {
        int c = str[i];
        switch (c)
        {
            case '-':
                if (i == 0) sign = -1;
                else return -1;
                break;
            default:
                if (c >= '0' && c <= '9')   ret += (c - '0') * place;
                else return -1;
        }
    }

    return sign * ret;
}

int strtoint(char* str)
{
    char* temp = str;
    int n = 0;
    while (*temp != '\0')
    {
        n++;
        temp++;
    }
    return strtoint_n(str, n);
}

Ref: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html

Amith Chinthaka
fonte
1
Por que fazer isso? Um dos maiores problemas com os atoiamigos e é que, se houver excesso, é um comportamento indefinido. Sua função não verifica isso. strtole amigos fazem.
chad
1
Sim. Como C não é Python, espero que as pessoas que usam a linguagem C estejam cientes desse tipo de erro de estouro. Tudo tem seus próprios limites.
Amith Chinthaka
-1

Se a resposta ajudou de alguma forma, por favor, marque como resposta, caso a sua dúvida não tenha sido solucionada, por favor, poste novamente.

void splitInput(int arr[], int sizeArr, char num[])
{
    for(int i = 0; i < sizeArr; i++)
        // We are subtracting 48 because the numbers in ASCII starts at 48.
        arr[i] = (int)num[i] - 48;
}
Khaled Mohammad
fonte
-1
//I think this way we could go :
int my_atoi(const char* snum)
{
 int nInt(0);
 int index(0);
 while(snum[index])
 {
    if(!nInt)
        nInt= ( (int) snum[index]) - 48;
    else
    {
        nInt = (nInt *= 10) + ((int) snum[index] - 48);
    }
    index++;
 }
 return(nInt);
}

int main()
{
    printf("Returned number is: %d\n", my_atoi("676987"));
    return 0;
}
Aditya Kumar
fonte
O código não é compilado em C. Por que nInt = (nInt *= 10) + ((int) snum[index] - 48);vs. nInt = nInt*10 + snum[index] - '0'; if(!nInt)não é necessário.
chux - Restabelece Monica
-3

No C ++, você pode usar essa função:

template <typename T>
T to(const std::string & s)
{
    std::istringstream stm(s);
    T result;
    stm >> result;

    if(stm.tellg() != s.size())
        throw error;

    return result;
}

Isso pode ajudá-lo a converter qualquer string para qualquer tipo, como float, int, double ...

neodelphi
fonte
1
Já existe uma pergunta semelhante sobre o C ++ , onde os problemas com essa abordagem são explicados.
Ben Voigt
-6

Sim, você pode armazenar o número inteiro diretamente:

int num = 45;

Se você precisar analisar uma string atoiou strolvencer o concurso "menor quantidade de código".

Yann Ramin
fonte
Se você deseja fazê-lo com segurança, strtol()na verdade , exige uma quantidade razoável de código. Ele pode retornar LONG_MINou LONG_MAX seja , se é isso o valor convertido real ou se há um estouro negativo ou estouro, e ele pode retornar 0, quer se esse é o valor real ou se não houve número a ser convertido. Você precisa definir errno = 0antes da chamada e verificar o endptr.
21411 Keith Thompson
As soluções fornecidas para analisar não são soluções viáveis.
precisa saber é o seguinte