8

I'm working on an assignment and as part of it I need to extract the integer from a string.

I've tried using the atoi() function, but it always returns a 0, so then I switched up to strtol(), but it still returns a 0.

The goal is to extract the integers from the string and pass them as arguments to a different function. I'm using a function that then uses these values to update some data (update_stats).

Please keep in mind that I'm fairly new to programming in the C language, but this was my attempt:

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);
}

The purpose of this is in a string "e5 d8" (for example) I would extract the 5 and the 8 from that string.

The format of the string is always the same.

How can I do this?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Ramux05
  • 332
  • 1
  • 10
  • 1
    There's no obvious bug in this part of the code, so the problem can't be reproduced. – Lundin Mar 25 '20 at 14:02
  • 1
    You could use loops to skip over all non-digit characters using `isdigit` before you use `strtol` – Bodo Mar 25 '20 at 14:02
  • 1
    Both `atoi` and `strtol` expect to receive the pointer to the first character of a numeral—your pointer must already be pointing to a digit. Neither “e” nor “d” is a digit when the base is 10. To find the numerals in the string, you should write code to examine each character to determine whether it is a digit or not. Once you have found a digit, you can convert either it (just the one digit) or the sequence of digits (several digits) there to a number, depending on what your need is. – Eric Postpischil Mar 25 '20 at 14:03
  • @Lundin: There is a perfectly obvious bug; passing a pointer to the first character of `"e5 d8"` to `strtol` is not a correct way to find the “5” and convert it to a number. – Eric Postpischil Mar 25 '20 at 14:04
  • @EricPostpischil Indeed, if that is the actual input. Hard to tell without the calling code. – Lundin Mar 25 '20 at 14:06

3 Answers3

8

strtol doesn't find a number in a string. It converts the number at the beginning of the string. (It does skip whitespace, but nothing else.)

If you need to find where a number starts, you can use something like:

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

(man strpbrk)

If your numbers might be signed, you'll need something a bit more sophisticated. One way is to do the above and then back up one character if the previous character is -. But watch out for the beginning of the string:

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

Just putting - into the strpbrk argument would produce false matches on input like non-numeric7.

rici
  • 234,347
  • 28
  • 237
  • 341
2

If the format is always like this, then this could also work

#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;
}

Output

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

%[^0-9] will match any non digit character. By adding the * like this %*[^0-9] indicates that the data is to be read from the string, but ignored.

Eraklon
  • 4,206
  • 2
  • 13
  • 29
  • Nice use of `sscanf`. I really appreciate the effort. However, it's nowhere specified that the format is the way you have taken for granted. Try to add a little more genericity if you can. Also, if the user is stuck in I/O and string to integer conversion, there's a high probability, that he/she's new. So, it would be really helpful to add or at least provide a link to the documentation, e.g., in this case, `sscanf`. Nice answer though :) – kesarling He-Him Mar 26 '20 at 05:50
  • 1
    Well thanks. But also note that the question say "The format of the string is always the same.", which implies this could be the same. Genericity is a good thing, but the absence of it let solution works like this. The very first hit on google for `sscanf` is the documentation. I think what really be improvment here is little explanation of the format, which I may add here. – Eraklon Mar 26 '20 at 09:29
  • Yep true. Sorry. I guess I missed the last statement. Nice answer though :) – kesarling He-Him Mar 26 '20 at 13:40
1

I suggest you write the logic on your own. I know, it's like reinventing the wheel, but in that case, you will have an insight into how the library functions actually work.

Here is a function I propose:

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;
}

Don't forget to pass a string with a \0 at the end :)

kesarling He-Him
  • 1,944
  • 3
  • 14
  • 39