Search code examples
cstringscanfstdin

C scanf() scansets to read comma separated value (string and integer) from console


I have following task:

Read comma separated data in below format:
string1, string2, integer_number
e.g:

  Hi, Hello, 123
  John, Doe, 45

I wrote the below code to handle that case. The problem is that I have to distinguish the following cases:

  • asd, asd, asd
  • asd, asf
    But in these two cases, scanf returns value 2 which is proper behavior because as I understand firstly there is no integer given, secondly there is no third value. So, I need some help how to set the scanset to only pass as proper string data when it consists of some text and comma -> "text,". Or maybe I should use different function than scanf().

What makes this task harder is requirement to not use any additional arrays declared by [] or dynamically allocated. This has to be done only using those 3 variables. No other buffers allowed.

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

int main()
{
    char string1[20];
    char string2[20];
    int number;
    printf("Type data:\n");
    int ret_val = scanf("%[a-zA-Z], %[a-zA-Z], %d", string1, string2, &number);
    if (ret_val != 3) {
        printf("ret_val %d", ret_val);
        return 1;
    }
    printf("%s %s %d\n", string1, string2, number);
    return 0;
}

Solution

  • The trick I usually rely on with *scanf() is using %n in order to detect that the input was read till this point; this is particularly useful when you expect some literals (they are not considered in the result of *scanf()).

    Note that according to man 3 sscanf, %n are not considered in the result of *scanf().

    /**
      gcc -std=c99 -o prog_c prog_c.c \
          -pedantic -Wall -Wextra -Wconversion \
          -Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
          -g -O0 -UNDEBUG -fsanitize=address,undefined
    **/
    
    #include <stdio.h>
    
    void
    parse_line(const char *line)
    {
      printf("[%s] --> ", line);
      char string1[20]="";
      char string2[20]="";
      int number=-1;
      int n1=-1, n2=-1;
      int r=sscanf(line, "%[a-zA-Z],%n %[a-zA-Z],%n %d",
                   string1, &n1, string2, &n2, &number);
      switch(r)
      {
        case 3:
        {
          printf("3 fields extracted: %s %s %d\n", string1, string2, number);
          break;
        }
        case 2:
        {
          if(n2!=-1) // this coma has been read
          {
            printf("incorrect third field, ");
          }
          printf("2 fields extracted: %s %s\n", string1, string2);
          break;
        }
        case 1:
        {
          if(n1!=-1) // this coma has been read
          {
            printf("incorrect second field, ");
          }
          printf("1 field extracted: %s\n", string1);
          break;
        }
        default:
        {
          printf("no field extracted\n");
        }
      }
    }
    
    int
    main(void)
    {
      parse_line("Hi, Hello, 123");
      parse_line("John, Doe, 45");
      parse_line("asd, asd, asd");
      parse_line("asd, asf");
      parse_line("asd, ");
      parse_line("asd");
      parse_line("");
      return 0;
    }
    /**
    [Hi, Hello, 123] --> 3 fields extracted: Hi Hello 123
    [John, Doe, 45] --> 3 fields extracted: John Doe 45
    [asd, asd, asd] --> incorrect third field, 2 fields extracted: asd asd
    [asd, asf] --> 2 fields extracted: asd asf
    [asd, ] --> incorrect second field, 1 field extracted: asd
    [asd] --> 1 field extracted: asd
    [] --> no field extracted
    **/