Home > Blockchain >  Parse the string into integers and store them in array (range values) in C
Parse the string into integers and store them in array (range values) in C

Time:04-19

{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.

  • Related