3

I want to read a list of space - separated numbers stored in a string.
I know how this can be done using stringstream in C++ -:

string s = "10 22 45 67 89";
stringstream ss(s);
int numbers[100];
int k=0, next_number;
while(ss>>next_number)
    numbers[k++]=next_number;

My question is how can we do this in C ? I have read about sscanf() in C but I am not certain it can be useful here.

David G
  • 94,763
  • 41
  • 167
  • 253
Anmol Singh Jaggi
  • 8,376
  • 4
  • 36
  • 77

5 Answers5

8

Use sscanf() and "%n to know where to begin the next scan.

char *s = "10 22 45 67 89";
for (k=0; k<100; k++) {
  int n;
  int cnt = sscanf(s, "%d %n", numbers[k], &n);
  if (cnt != 1) break;
  s += n;
}
if (*s) Handle_DidNotEndCleanly();
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
6

You can use sscanf here if you know the number of items you expect in the string:

char const *s = "10 22 45 67 89";

sscanf(s, "%d %d %d %d %d", numbers, numbers+1, numbers+2, numbers+3 numbers+4);

If, however, you don't know the number of items a priori, you might try tokenizing the input, then attempting to convert each item individually.

char s[] = "10 22 45 67 89";

char *n = strtok(s, " ");
do { 
    numbers[k++] = atoi(n);
} while (n = strtok(NULL, " "));

Note that strtok has quite an ugly interface--it modifies the string you pass to it, and requires that you use it roughly as above--you pass the input string in the first call, then pass NULL for the string to tokenize for subsequent calls until you reach the end of the string. It stores a pointer to the current location in the string internally, which also makes thread safety a problem.

You can also use sscanf with the %n conversion to count the number of characters already converted, but that's (IMO) in nearly direct competition with strtok for some of the ugliest code imaginable, and I can't handle posting two things like that in one day.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

If the order of the numbers does not matter, you can loop through the string backwards, one character at a time.

char s[] = "10 22 45 67 89";
int i;
int tmp=0;
int mult=1;
for(i=strlen(s)-1; i>=0; i--) {
    if(s[i] != ' ') {
        tmp += (s[i]-'0') * mult;
        mult *= 10;
    } else {
        // your number is stored in tmp, do whatever you want with it
        tmp=0;
        mult=1;
    }
}

If you must loop through it in the forward direction, this method can still be used but you need more code.

This method does not require you to know the number of items in the string beforehand.

user12205
  • 2,684
  • 1
  • 20
  • 40
0
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>

typedef struct ss {//Simple stringstream mock 
    const char *p;
} Ss;

Ss *ss_new(const char *s){
    Ss *ssp = malloc(sizeof(*ssp));
    ssp->p = s;
    return ssp;
}

bool ss_nextInt(Ss *ssp, int *v){
    char *endp;
    while(isspace(*ssp->p))
        ssp->p++;
    if(!*ssp->p)
        return false;
    *v = strtol(ssp->p, &endp, 10);
    if(ssp->p != endp){
        ssp->p = endp;
        return true;
    }
    return false;
}
int main(){
    const char *s = "10 22 45 67 89";
    int numbers[100];
    int i, k=0, next_number;
    Ss *ssp =  ss_new(s);
    while(ss_nextInt(ssp, &next_number))
        numbers[k++]=next_number;
    free(ssp);
    for(i = 0; i < k; ++i)
        printf("%d\n", numbers[i]);
    return 0;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
0

In <string.h> there are numerous options to split strings. strchr, strspn, and strpbrk to name a few. Along with the C90 function strtol for parsing numbers, defined in <stdlib.h>.

strpbrk and strtol

#include <stdlib.h> // strtol
#include <string.h> // strpbrk
...
const char *s = "10 22 45 67 89";
char *p = s;
int numbers[MAX_NUMBERS];
size_t i = 0;
do {
  p = strpbrk(p, "-0123456789");
  if (p == NULL) break;
  char *ptr;
  int n = (int)strtol(p, &ptr, 10);
  if (p == ptr) break; // in case *p == '-' and p[1] is not a number
  numbers[i++] = n;
} while (i < MAX_NUMBERS);

strpbrk take two strings and returns a pointer to the first occurrence of any of the characters provided in the second string, or NULL if none are found.

strtol takes a string, a pointer to fill, and a base. It returns a long parsed from the first string, and fills the pointer with the location of the end of the number, or the original string pointer if no number was read.

kdhp
  • 2,096
  • 14
  • 15