Como posso extrair um número inteiro de dentro de uma string?

8

Estou trabalhando em uma tarefa e, como parte dela, preciso extrair o número inteiro de uma string.

Eu tentei usar a atoi()função, mas ela sempre retorna a 0, então mudei para strtol(), mas ainda retorna a 0.

O objetivo é extrair os números inteiros da string e passá-los como argumentos para uma função diferente. Estou usando uma função que usa esses valores para atualizar alguns dados ( update_stats).

Lembre-se de que eu sou bastante novo em programação na linguagem C, mas essa foi minha tentativa:

void get_number (char str[]) {
    char *end;
    int num;
    num = strtol(str, &end, 10);
    update_stats(num);
    num = strtol(end, &end, 10);
    update_stats(num);
}

O objetivo disso é em uma string "e5 d8"(por exemplo), eu extrairia oe 5a 8partir dessa string.

O formato da string é sempre o mesmo.

Como posso fazer isso?

Ramux05
fonte
1
Não há nenhum erro óbvio nesta parte do código, portanto o problema não pode ser reproduzido.
Lundin
1
Você pode usar loops para pular todos os caracteres que não sejam dígitos isdigitantes de usarstrtol
Bodo
1
Ambos atoie strtolesperam receber o ponteiro para o primeiro caractere de um numeral - seu ponteiro já deve estar apontando para um dígito. Nem "e" nem "d" são um dígito quando a base é 10. Para encontrar os números na sequência, você deve escrever um código para examinar cada caractere para determinar se é um dígito ou não. Depois de encontrar um dígito, você pode convertê-lo (apenas um dígito) ou a sequência de dígitos (vários dígitos) para um número, dependendo da sua necessidade.
Eric Postpischil 25/03
@Lundin: Existe um bug perfeitamente óbvio; passar um ponteiro para o primeiro caractere de "e5 d8"to strtolnão é uma maneira correta de encontrar o “5” e convertê-lo em um número.
Eric Postpischil 25/03
@EricPostpischil De fato, se essa é a entrada real. Difícil dizer sem o código de chamada.
Lundin

Respostas:

7

strtolnão encontra um número em uma string. Ele converte o número no início da string . (Ignora o espaço em branco, mas nada mais.)

Se você precisar descobrir onde um número começa, use algo como:

const char* nump = strpbrk(str, "0123456789");
if (nump == NULL) /* No number, handle error*/

( man strpbrk)

Se seus números puderem ser assinados, você precisará de algo um pouco mais sofisticado. Uma maneira é fazer o acima e fazer backup de um caractere se o caractere anterior for -. Mas cuidado com o início da string:

if ( nump != str && nump[-1] == '-') --nump;

Apenas colocar -o strpbrkargumento produziria correspondências falsas em entradas como non-numeric7.

rici
fonte
2

Se o formato é sempre assim, isso também pode funcionar

#include <stdio.h>

int main()
{
    char *str[] = {"a5 d8", "fe55 eec2", "a5 abc111"};
    int num1, num2;

    for (int i = 0; i < 3; i++) {
      sscanf(str[i], "%*[^0-9]%d%*[^0-9]%d", &num1, &num2);
      printf("num1: %d, num2: %d\n", num1, num2);
    }
    return 0;
}

Resultado

num1: 5, num2: 8                                                                                                                                                                   
num1: 55, num2: 2                                                                                                                                                                  
num1: 5, num2: 111

%[^0-9]corresponderá a qualquer caractere que não seja um dígito. Ao adicionar o *mesmo, isso %*[^0-9]indica que os dados devem ser lidos a partir da string, mas ignorados.

Eraklon
fonte
Bom uso de sscanf. Eu realmente aprecio o esforço. No entanto, em nenhum lugar é especificado que o formato seja o caminho que você tomou como garantido. Tente adicionar um pouco mais de generosidade, se puder. Além disso, se o usuário estiver preso na E / S e na conversão de string para inteiro, há uma alta probabilidade de que ele seja novo. Portanto, seria realmente útil adicionar ou pelo menos fornecer um link para a documentação, por exemplo, neste caso sscanf,. Boa resposta embora :)
d4rk4ng31 26/03
1
Bem, obrigado. Mas observe também que a pergunta diz "O formato da string é sempre o mesmo.", O que implica que pode ser o mesmo. Genericidade é uma coisa boa, mas a ausência dela permite que a solução funcione assim. O primeiro hit no google para sscanfé a documentação. Eu acho que o que realmente é o melhoramento aqui é pouca explicação do formato, que posso acrescentar aqui.
Eraklon 26/03
Sim verdade. Desculpa. Acho que perdi a última declaração. Boa resposta embora :)
d4rk4ng31
2

Eu sugiro que você escreva a lógica por conta própria. Eu sei, é como reinventar a roda , mas nesse caso, você terá uma ideia de como as funções da biblioteca realmente funcionam.

Aqui está uma função que proponho:

bool getNumber(str,num_ptr)
char* str;
long* num_ptr;
{
    bool flag = false;
    int i = 0;
    *num_ptr = 0;
    char ch = ' ';
    while (ch != '\0') {
        ch = *(str + i);
        if (ch >= '0' && ch <= '9') {
            *num_ptr = (*num_ptr) * 10 + (long)(ch - 48);
             flag = true;
        }
        i++;
    }
    return flag;
}

Não se esqueça de passar uma string com a \0no final :)

d4rk4ng31
fonte
Esta função varre toda a cadeia e retorna um número que consiste nos dígitos em ordem.
d4rk4ng31 25/03
considerando c, você pode converter bool para int truee falsepara 1e 0respectivamente
d4rk4ng31