1

I am trying to read only the integers from a string. The whitespace and non-integer characters are essentially random with each iteration. Ideally my code would run through this and print(or otherwise store) only the integers. So for this test case it would be 6,1,0,16,2,3 etc etc.

char line [ 200 ] ="T06      1/ 0 16/ 2  3/15 41/40 17/36  0/11  0/11  0/11  1/14  1/ 7";
int num=0;
int count=0;
int i=0;
char *str = line;   

while (sscanf(str, "%d%n", &num,&i)){
      printf ("number: %d\n",num);
      str+=i;
      count++;
}

Clearly its not working and I think I need a way for sscanf to skip anything that is not 0-9. Any help?

Thanks

6 Answers6

3

change to

while (sscanf(str, "%*[^0-9]%d%n", &num, &i)==1){
    printf ("number: %d\n",num);
    str+=i;
    count++;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
2

Replace all non-digit characters with whitespace and then just scanf them all. You'll need to make adjustments for negatives but I tested this and it gives the output you want for the given input.

char line [ 200 ] ="T06      1/ 0 16/ 2  3/15 41/40 17/36  0/11  0/11  0/11  1/14  1/ 7";
int num=0;
int count=0;
int i=0;
char *str = line; 

for (int i = 0; i < strlen(line); i++)
    if (!isdigit(line[i]))
        line[i] = ' ';

while (sscanf(str, "%d%n", &num,&i) == 1){
      printf ("number: %d\n",num);
      str+=i;
      count++;
}
dreamlax
  • 93,976
  • 29
  • 161
  • 209
1
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>

enum oflow { OFL_GOOD, OFL_OVER, OFL_UNDER };

int getnums(const char *str, int nums[], enum oflow oflow[], int n)
{
  int i;

  for (i = 0; *str; ) {
    char *junk;
    long val;

    str += strcspn(str, "0123456789+-");

    if (!*str)
      break;

    errno = 0;
    val = strtol(str, &junk, 10);

    if (junk > str) {
      if (i < n) {
        if (((val == LONG_MIN) && errno == ERANGE) || (val < INT_MIN)) {
          nums[i] = 0;
          oflow[i++] = OFL_UNDER;
        } else if (((val == LONG_MAX) && errno == ERANGE) || (val > INT_MAX)) {
          nums[i] = 0;
          oflow[i++] = OFL_OVER;
        } else {
          oflow[i] = OFL_GOOD;
          nums[i++] = val;
        }
      } else {
        i++;
      }
      str = junk;
    } else {
      str++; /* no number was pulled out: skip one char and try again */
    }
  }

  return i;
}

int main(int argc, char **argv)
{
  int nums[256];
  enum oflow oflow[256];

  if (argc > 1) {
    int n = getnums(argv[1], nums, oflow, 256);
    int i;

    if (n > 256) {
      printf("array is too small: we need %d elements\n", n);
      n = 256;
    }

    for (i = 0; i < n; i++) {
      if (oflow[i])
        printf("nums[%d] = %sflow\n", i, oflow[i] == OFL_OVER ? "over" : "under");
      else
        printf("nums[%d] = %d\n", i, nums[i]);
    }
  }

  return 0;
}

tests:

$ ./nums ""
$ ./nums "0"
nums[0] = 0
$ ./nums "-"
$ ./nums "+"
$ ./nums "+1"
nums[0] = 1
$ ./nums "-1"
nums[0] = -1
$ ./nums "abc"
$ ./nums "asd+asdf+1  -0+1-3234abc-10zzz-"
nums[0] = 1
nums[1] = 0
nums[2] = 1
nums[3] = -3234
nums[4] = -10
$ ./nums "+++-1+++"
nums[0] = -1
$ ./nums "++"
$ ./nums "1+11111111111111111111111-111111111111111111111"
nums[0] = 1
nums[1] = overflow
nums[2] = underflow
$ ./nums "$(seq 1 300)" | head -5
array is too small: we need 300 elements
nums[0] = 1
nums[1] = 2
nums[2] = 3
nums[3] = 4
$ ./nums "$(seq 1 300)" | tail -5
nums[251] = 252
nums[252] = 253
nums[253] = 254
nums[254] = 255
nums[255] = 256

Homework exercise: in what situations is junk == str after the strtol, so that the str++ case executes? Why do we need this logic?

Kaz
  • 55,781
  • 9
  • 100
  • 149
0

To filter everything but the digits, use strcspn() and sscanf() in a loop:

int i;
while (sscanf(str += strcspn(str, "0123456789"), "%d%n", &num, &i) == 1) {
    printf("number: %d\n", num);
    count++;
    str += i;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
-1

sscanf is really a pain to use, and it's even worse if your input is not the same every time. You might try regular expressions, or you might try to parse out the integers by hand doing something like this:

int i;
int num = 0;
int num_valid = 0;
int count = 0;
for(i = 0; line[i]; i++) {  // loop through every character
    if(line[i] >= '0' && line[i] <= '9') { // check to see if it's a digit
        num *= 10;                   // scale up num
        num += lin[i] - '0';         // convert ASCII to INT
        num_valid = 1;               // indicate that num contains a real number

    } else if(num_valid) {           // if the number is done
        printf ("number: %d\n",num);
        count++;
        num = 0;
        num_valid = 0;
    }
}
Luke
  • 708
  • 5
  • 13
-1

OP almost had it!

Add tests for end-of-string and return value from sscanf().

char line[200] = "T06 1/ 0 16/ 2  3/15 41/40 17/36  0/11  0/11  0/11  1/14  1/ 7";
int num;
int count = 0;
char *str = line;   

while (*str) {
  int i; // Place to save count of characters scanned.
  if (1 == sscanf(str, "%d%n", &num, &i)) {
    printf("number: %d\n", num);
    count++;
    str += i;
  } else {
    str++; // Go to the next character
  }
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256