Search code examples
cwhitespacescanfgets

C : scanf and whitespaces. I've tried almost everything , currently learning about struct


I searched all the web but I haven't come to a real solution (maybe I haven't googled the right words, I don't know). but here we are: this is my first C project with structs, and I tried to use gets and puts to overcome the printf-scanf's problem with white spaces, but the gets led to a lot of problems (the program skipped directly to the puts, without the gets) so I come in a topic of another guest of this forum where was mentioned

scanf("%[^\n]",str);

I applied this to my code, but I get the same problem, and I have also tried

scanf("%[^\n]s",str);
scanf("%[^\n]",&str); //despite looked senseless to me

I had read about fgets and fscanf, but I'm on an earlier phase

Here is the code... I use Visual Studio 2015, running on Windows 10.

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

typedef struct s_libro {
    char titolo[50];
    char autore[50];
    char editore[20];
    int pagine;
    int costo$;
}t_libro;

int main() {
    t_libro libro1;
    t_libro libro2;
    printf("Inserire autore(max 50) ,titolo(max 50) , editore(max 20), pagine, costo$\n");
    //LIBRO 1
    printf("\nLIBRO 1\n");

    printf("\nInserisci il nome dell'autore del libro1 : \n");
    scanf("%[^\n]", libro1.autore);

    printf("\nInserire titolo del libro1 : \n");
    scanf("%[^\n]", libro1.titolo);

    printf("\nInserire nome dell'editore del libro1:\n");
    scanf("%[^\n]", libro1.editore);

    printf("\nInserire numero di pagine del libro1 : \n");
    scanf("%d", &libro1.pagine);

    printf("\nInserire il costo (dato numerico) del libro1 : \n");
    scanf("%d", &libro1.costo$);

    printf("\nLIBRO1\t");
    printf("\nTitolo Libro1\t  %s", libro1.titolo);
    printf("\nAutore Libro1\t  %s", libro1.autore);
    printf("\nPagine Libro1\t  %d", libro1.pagine);
    printf("\nEditore Libro1\t  %s", libro1.editore);
    printf("\nCosto Libro1\t  %d $", libro1.costo$);

    printf("\n\n");


    //LIBRO 2

    printf("\nLIBRO 2\n");

    printf("\nInserisci il nome dell'autore del libro2 : ");
    scanf("%[^\n]", &libro2.autore);

    printf("\nInserire il titolo del libro2 : ");
    scanf("%[^\n]", &libro2.titolo);

    printf("\nInserire L'editore del libro2:");
    scanf("%[^\n]", libro2.editore);

    printf("\nInserire il numero di pagine del libro2 : ");
    scanf("%d", &libro2.pagine);

    printf("\nInserire il costo (dato numerico) del libro2 : ");
    scanf("%d", &libro2.costo$);

    printf("\nLIBRO2\t");
    printf("\nTitolo Libro2\t  %s", libro2.titolo);
    printf("\nAutore Libro2\t  %s", libro2.autore);
    printf("\nPagine Libro2\t  %d", libro2.pagine);
    printf("\nEditore Libro2\t  %s", libro2.editore);
    printf("\nCosto Libro2\t  %d $", libro2.costo$);

    printf("\n\n");
}

Solution

  • You have two problems:

    1. You don't check that scanf() succeeds.
    2. You don't allow for the fact that scanf() leaves the newlines in the input stream.

    You have code such as:

    printf("\nInserisci il nome dell'autore del libro1 : \n");
    scanf("%[^\n]", libro1.autore);
    
    printf("\nInserire titolo del libro1 : \n");
    scanf("%[^\n]", libro1.titolo);
    

    The first of these should work. The second will fail, because the first left a newline in the input stream, and the scan set doesn't recognize the newline as valid, so it fails.

    There are several possible solutions. Which is best depends on your detailed requirements.

    1. Use standard C fgets() or perhaps POSIX getline() to read lines and then sscanf() to process the data.
    2. Use " %49[^\n]" in the format string to skip white space including newlines before reading up to 49 non-newlines into the variable. This is the minimal change. The length limitation prevents buffer overflows. Yes, the size in the format string doesn't include the null byte.
    3. Read the residue of the line after each input with scanf(). For example:

      static inline void gobble(void)
      {
          int c;
          while ((c = getchar()) != EOF && c != '\n')
              ;
      }
      

      and then:

      printf("\nInserisci il nome dell'autore del libro1 : \n");
      if (scanf("%49[^\n]", libro1.autore) != 1)
          …process error or EOF…
      gobble();
      
      printf("\nInserire titolo del libro1 : \n");
      if (scanf("%49[^\n]", libro1.titolo) != 1)
          …process error or EOF…
      gobble();
      

    There are no doubt many other options; you will have to decide what works best for you. Note that you should check the status of scanf() each time you use it, and you should make sure that you do not allow overflows.


    Incidentally, the scanf("%[^\n]", &str) variant is wrong. The type of the argument passed is char (*)[50] (if the variable is char str[50];), but the scan set conversion specification expects a char * — a quite different type. There's a quirk that means you get away with it; the address value of str and &str is the same, but the type is different, as could be noted by looking at (str + 1) and &str + 1 — the values will be quite different. The correct notation is scanf("%[^\n]", str), therefore (modulus adding overflow protection).