0

I've been trying to come up with solution for reading input which contains string and then brackets with array of numbers (I don't know how many numbers will be inputed.

Input could look like:

sacrifice (1, 2, 4, 2)

I am wondering if it is possible to achieve with scanf. I've been looking for different functions such as getline, sscanf, fgets and so on. But I couldn't come up with solution.

My code looks like this:

    scanf("%[^(]", command);
    while ( ( c = getchar() ) != ')' )
    {
        scanf("%d", weights[pos]);
        pos++;
    }

Which should read string until the bracket is found and then I tried to load the numbers in array as long as it doesn't reach the ')'. Yet it doesn't seem to work.

Is scanf viable to achieve this? Could anyone point me in better direction if not please?

Pernick
  • 33
  • 2
  • 9
  • When you use `getchar` to search for the ending parentheses, you *extract* the characters from the input buffer and lose them. Also remember that the arguments for `scanf` needs to be *pointers*. – Some programmer dude Dec 07 '16 at 13:55
  • So if I ask if c = getchar() is not ')' I can no longer scan it after? – Pernick Dec 07 '16 at 13:58
  • You are losing characters for using `getchar`. You have to use that `c` (variable) into which you have `getchar()`-ed within `while` body. – Surajeet Bharati Dec 07 '16 at 13:59
  • Possible duplicate http://stackoverflow.com/questions/2082743/c-equivalent-to-fstreams-peek – n. m. could be an AI Dec 07 '16 at 14:06
  • It gives me segmentation fault when I try to do that. Is there a way I can store those numbers somehow and then create dynamically allocated array based on how many numbers I have? – Pernick Dec 07 '16 at 14:18
  • `scanf("%d", weights[pos]);` --> `scanf("%d", &weights[pos]);` – BLUEPIXY Dec 08 '16 at 02:34

3 Answers3

0

I think it would be simpler to read the complete line from stdin and then parse it by hand using strtok or strcspn. Something like below could be done.

Disclaimer: This is just some sample code and doesn't handle all possible inputs and will crash with invalid input, it is just to give you an idea about how to do it. If you want to go this way, you would have to handle various error conditions, such as:

  • checking return value of malloc/getline/realloc
  • instead of atoi using a better function like strtol (which allows error checking),
  • handling white spaces in the input and
  • handling input which does not contain any parenthesis

Those are some of the many things which you would have to think about.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int *parse_numbers(char *numstr, size_t *size)
{
    char *p;
    char *s = numstr;
    char *last;
    size_t array_size = 10;
    int *numbers = malloc(sizeof(int) * array_size);
    size_t offset = 0;
    for (p = strtok_r(s, ",", &last); p; p = strtok_r(NULL, ",", &last)) {
        if (offset == array_size) {
            array_size *= 2;
            numbers = realloc(numbers, sizeof(int) * array_size);
            //TODO do error check
        }
        numbers[offset++] = atoi(p); //strtol would be a better choice
    }
    *size = offset;
    return numbers;
}

int main()
{
    char *s = NULL;
    char *p;
    char *last;
    int i = 0;
    int *numbers;
    size_t size;
    size_t linesize = 0;

    getline(&s, &linesize, stdin);
    for (p = strtok_r(s, "(", &last); p; p = strtok_r(NULL, "(", &last)) {
        if (i++ == 0) {
            //This is the part of the string before '('
            cmd = p;
        } else {
            // This is the part of the string after '('
            numbers = parse_numbers(p, &size);
        }
    }
    for (i = 0; i < size; i++) {
        printf("%d\n", numbers[i]);
    }
    free(numbers);
    free(s);
    return 0;
}
Abhinav Upadhyay
  • 2,477
  • 20
  • 32
0

Separate input from parsing. Far easier to handle the various issues of command processing. Concerning "don't know how many numbers will be inputed", IMO, a reasonable upper bound should be established. Else code is susceptible to overwhelming memory resources due to user input - a hacker exploit.

char command[1000];
while (fgets(command, sizeof command, stdin)) {

Now process the command using sscanf(), strtok() or your own code. The best method depends on maybe things not posted by OP, especially error handling.

    int cmd_start;
    int cmd_end;
    int n = 0;
    // sacrifice (1, 2, 4, 2, ...)
    //               +----------------- skip optional white space
    //               |+---------------- record scan position 
    //               || +-------------- scan, but not save, lower case letters
    //               || |      +------- record scan position 
    //               || |      | +----- skip optional white space
    //               || |      | |+---- scan (
    //               || |      | ||+--- skip optional white space
    //               || |      | |||+-- record scan position 
    sscanf(command, " %n%*[a-z]%n ( %n", &cmd_start, &cmd_end, &n);
    if (n == 0) {
      printf("Invalid command '%s'\n", command);
      continue;
    }
    int x[sizeof command / 2];
    int x_count = 0;
    char *p = &command[n];  // pick up where previous scan ended.

    char sep[2] = {0};
    while (sscanf(p, "%d %1[,)] %n", &x[x_count], sep, &n) == 2) {
      x_count++;
      p += n;
      if (sep[0] == ')') break;
    }

    if (*p || sep[0] != ')') {
      printf("Invalid separator '%s'\n", command);
      continue;
    }

    // Use command 
    command[cmd_end] = '\0';
    Process_Command(&command[cmd_start], x, x_count);
  }
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

scanf("%d", weights[pos]); --> scanf("%d", &weights[pos]); – BLUEPIXY

That's indeed adequate to make the code work, provided a sufficiently dimensioned weights array.

Armali
  • 18,255
  • 14
  • 57
  • 171