{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
CodePudding user response:
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.