'Parse the string into integers and store them in array (range values) in C
{s20-s23,s25-s28,s30,s31}
The above is the input string which needs to parsed and stored in array. The array should be as follows-
20, 22, 23, 25, 26, 27, 28, 30, 31
i.e the values within the ranges should also be stored(s20-s23, s25-s28). I tried to do same but could not find a way. Please refer the following program and suggest the changes to get the output same. Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void) {
char *str = "{s20-s23,s30,s31}", *p = str;
int arr[100];
int i = 0;
int flag = 0;
while (*p) {
if (isdigit(*p)) {
int val = strtol(p, &p, 10);
arr[i] = val;
i++;
printf("%ld\n", val);
} else {
p++;
}
}
printf("\n");
for(int j=0; j<i;j++){
printf("%d\n",arr[j]);
}
return 0;
}
The output of above program is (values in the range are missing)
20, 23, 25, 28, 30, 31
Solution 1:[1]
Your input "{s20-s23,s25-s28,s30,s31}" is a string that starts with the { followed by by zero or mores ranges then a terminating term }. Each range is a cell with an optional - followed by another cell and a separator , unless it's the last range. A cell is a s followed by an unsigned number. This discussion would an informal specified grammar. Then you just implemented a function to process each term:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char *parse_chr(const char *s, char c) {
if(*s != c) return NULL;;
return s + 1;
}
const char *parse_cell(const char *s, unsigned *v) {
s = parse_chr(s, 's');
if(!s) return s;
// assumes values is between 0 and UINT_MAX
*v = strtol(s, (char **) &s, 10);
return s;
}
const char *parse_range(const char *s, unsigned *start, unsigned *end) {
s = parse_cell(s, start);
if(!s) return s;
if(*s != '-') {
*end = *start;
goto out;
}
s = parse_cell(s + 1, end);
if(!s) return s;
out:
return *s == ',' ? s + 1 : s;
}
// assumes v is sufficently large
const char *parse(const char *s, unsigned *n, unsigned *v) {
s = parse_chr(s, '{');
if(!s) return s;
*n = 0;
for(;;) {
if(*s != 's') break;
unsigned start, end;
s = parse_range(s, &start, &end);
if(!s || (start > end)) return s;
for(; start <= end; (*n)++, v++, start++) *v = start;
}
s = parse_chr(s, '}');
return parse_chr(s, '\0');
}
int main() {
unsigned values[100];
unsigned len;
if(!parse("{s20-s23,s25-s28,s30,s31}", &len, values)) {
printf("parse failed\n");
return 1;
}
for(int i = 0; i < len; i++) {
printf("%u%s", values[i], i + 1 < len ? ", " : "\n");
}
return 0;
}
This yield the expected result (assuming the missing 21 in the example output is a specification bug):
20, 21, 22, 23, 25, 26, 27, 28, 30, 31
I would prefer that parser() returns an array of struct ranges, then have another function expand that list. parser() should probably take a max length so we don't write too many elements to v perhaps like snprintf() allow v be NULL so you get a count. The other good option to have it allocate an array possible using realloc() to grow the array if needed. It would also be a good idea to improve error reporting so the parse tells you where and why it fails.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
