23

Say I have a string like ab234cid*(s349*(20kd and I want to extract all the numbers 234, 349, 20, what should I do ?

CDT
  • 10,165
  • 18
  • 66
  • 97

7 Answers7

38

You can do it with strtol, like this:

char *str = "ab234cid*(s349*(20kd", *p = str;
while (*p) { // While there are more characters to process...
    if ( isdigit(*p) || ( (*p=='-'||*p=='+') && isdigit(*(p+1)) )) {
        // Found a number
        long val = strtol(p, &p, 10); // Read number
        printf("%ld\n", val); // and print it.
    } else {
        // Otherwise, move on to the next character.
        p++;
    }
}

Link to ideone.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    +1: but why `long long val = strtol();`? I'd rather use C99's `strtoll()` or define val as `long` :) – pmg Nov 15 '12 at 14:39
  • Actually, since he's not scanning for `-`, I would `unsigned long` and `strtoul`. =) – Stephen Canon Nov 15 '12 at 14:40
  • Is sending the address of p to strtol as the end pointer, a good idea? – Happy Green Kid Naps Nov 15 '12 at 14:40
  • 2
    @HappyGreenKidNaps That is what makes the second parameter of `strtol` so useful. – Sergey Kalinichenko Nov 15 '12 at 14:42
  • @dasblinkenlight -- I don't read numbers from streams all that often, but all examples of `strto` family of functions that I have seen use a different variable for end pointer. They check for `errno` after the call, perform sanity checks on the return value to see if it is in the expected range, and only then overwrite the pointer to the string stream with the end pointer. – Happy Green Kid Naps Nov 15 '12 at 14:55
  • @HappyGreenKidNaps In this case you know the call is going to be successful, because you checked for `isdigit` before making the call. In general, though, I agree that sending in a separate pointer is more prudent. – Sergey Kalinichenko Nov 15 '12 at 15:06
  • 1
    @HappyGreenKidNaps: if the call is not successful because there is no digit present, the value of `p` won't be changed by the call (or rather, it will be updated with the same value it had previously). – Stephen Canon Nov 15 '12 at 15:41
  • 6
    how about if it has negative number , say ab-234cid*?? – Raulp Mar 12 '15 at 10:20
  • Can we solve this problem using N different threads if asked ? – Sagar Feb 28 '18 at 07:23
17

A possible solution using sscanf() and scan sets:

const char* s = "ab234cid*(s349*(20kd";
int i1, i2, i3;
if (3 == sscanf(s,
                "%*[^0123456789]%d%*[^0123456789]%d%*[^0123456789]%d",
                &i1,
                &i2,
                &i3))
{
    printf("%d %d %d\n", i1, i2, i3);
}

where %*[^0123456789] means ignore input until a digit is found. See demo at http://ideone.com/2hB4UW .

Or, if the number of numbers is unknown you can use %n specifier to record the last position read in the buffer:

const char* s = "ab234cid*(s349*(20kd";
int total_n = 0;
int n;
int i;
while (1 == sscanf(s + total_n, "%*[^0123456789]%d%n", &i, &n))
{
    total_n += n;
    printf("%d\n", i);
}
hmjd
  • 120,187
  • 20
  • 207
  • 252
3

here after a simple solution using sscanf:

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

char str[256]="ab234cid*(s349*(20kd";
char tmp[256];

int main()
{

    int x;
    tmp[0]='\0';
    while (sscanf(str,"%[^0123456789]%s",tmp,str)>1||sscanf(str,"%d%s",&x,str))
    {
        if (tmp[0]=='\0')
        {
            printf("%d\r\n",x);
        }
        tmp[0]='\0';

    }
}
MOHAMED
  • 41,599
  • 58
  • 163
  • 268
2

Make a state machine that operates on one basic principle: is the current character a number.

  • When transitioning from non-digit to digit, you initialize your current_number := number.
  • when transitioning from digit to digit, you "shift" the new digit in:
    current_number := current_number * 10 + number;
  • when transitioning from digit to non-digit, you output the current_number
  • when from non-digit to non-digit, you do nothing.

Optimizations are possible.

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57
  • 1
    I'm not familiar with state machines but this gives me some new ideas, thank you a lot! – CDT Nov 16 '12 at 13:20
1

If the numbers are seprated by whitespace in the string then you can use sscanf(). Since, it's not the case with your example, you have to do it yourself:

char tmp[256];

for(i=0;str[i];i++)
{
  j=0;
  while(str[i]>='0' && str[i]<='9')
  {
     tmp[j]=str[i];
     i++;
     j++;
  }
  tmp[j]=0;
  printf("%ld", strtol(tmp, &tmp, 10));
  // Or store in an integer array

}

P.P
  • 117,907
  • 20
  • 175
  • 238
1
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
void main(int argc,char *argv[])
{
char *str ="ab234cid*(s349*(20kd", *ptr = str;
while (*ptr) { // While there are more characters to process...
    if ( isdigit(*ptr) ) {
        // Found a number
        int val = (int)strtol(ptr,&ptr, 10); // Read number
        printf("%d\n", val); // and print it.
    } else {
        // Otherwise, move on to the next character.
        ptr++;
    }
}

}
Felix
  • 11
  • 1
  • Welcome to Stack Overflow! While this code snippet may solve the question, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, this reduces the readability of both the code and the explanations! – Filnor Nov 30 '18 at 12:41
0

Or you can make a simple function like this:

// Provided 'c' is only a numeric character
int parseInt (char c) {
    return c - '0';
}
  • If you decide to answer an older question that has well established and correct answers, adding a new answer late in the day may not get you any credit. If you have some distinctive new information, or you're convinced the other answers are all wrong, by all means add a new answer, but 'yet another answer' giving the same basic information a long time after the question was asked usually won't earn you much credit. – Jonathan Leffler Sep 02 '17 at 22:53