1

So I'm really confused. I have to write a program in C which is basically an address book, thus I have to get multiple strings from the user (name, ID, phone etc.)

In the beginning I tried using scanf() only but it messed up sometimes with the newline character '\n'. After some googling, I ended up using scanf() for getting single chars or ints (where user answers yes or no questions, or chooses an action from a menu) and fgets() to read the fields of the address book. However, I also had to use fflush(stdin) multiple times after using scanf() which is not recommended here as I have seen. This method worked so far as intended.

So what's the optimal way to read a string from the user? Does fflush(stdin) not offer portability? This is an assignment so I have to think for portability too, since I will execute my code on another computer.

Thank you in advance.

EDIT: So here is what I've got so far. Excuse some words that are written in another language (Albanian). I believe you can understand what's going on.

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

void regjistrim();
void kerkim();
void modifikim();
void fshirje();
void rradhitje();
void display();

#define EMRI 50
#define MBIEMRI 50
#define ID 20
#define TEL 20
#define EMAIL 25

typedef struct addressbook
{
    char emri[EMRI];
    char mbiemri[MBIEMRI];
    char id[ID];
    char tel[TEL];
    char email[EMAIL];
} addressbook;

FILE* Addressbook;

int main(void)
{
    char input[2];
    int choice;

    printf("----------------ADDRESS BOOK----------------");
    printf("\n\n\t1 - Regjistrimi i ri\n");
    printf("\n\t2 - Kerkim\n");
    printf("\n\t3 - Modifikim\n");
    printf("\n\t4 - Fshirje\n");
    printf("\n\t5 - Rradhitje\n");
    printf("\n\t6 - Afishim i address book\n");
    printf("\n\t0 - Exit\n");

    fgets(input, 2, stdin);
    sscanf(input, "%d", &choice);

    while (choice < 0 || choice > 6)
    {
        printf("\nShtypni nje numer nga 0 - 6: \n");
        fgets(input, 2, stdin);
        sscanf(input, "%d", &choice);
    }

    switch (choice)
    {
        case 1:
            regjistrim();
            break;
        case 2:
            kerkim();
            break;
        case 3:
            modifikim();
            break;
        case 4:
            fshirje();
            break;
        case 5:
            rradhitje();
            break;
        case 6:
            display();
            break;
        case 0:
            exit(0);
            break;
    }

    return 0;
}


//Regjistron nje qytetar ne addressbook
void regjistrim()
{
    char answer;

    addressbook entry;
    do
    {
        Addressbook = fopen("Addressbook.txt", "a+");

        printf("\nShtypni emrin: ");
        fgets(entry.emri, EMRI, stdin);

        printf("\nShtypni mbiemrin: ");
        fgets(entry.mbiemri, MBIEMRI, stdin);

        printf("\nShtypni ID-in: ");
        fgets(entry.id, ID, stdin);

        printf("\nShtypni nr. telefoni: ");
        fgets(entry.tel, TEL, stdin);

        printf("\nShtypni email-in: ");
        fgets(entry.email, EMAIL, stdin);

        fprintf(Addressbook, "Emri: %sMbiemri: %sID: %sNr. telefoni: %sEmail: %s\n", entry.emri, entry.mbiemri, entry.id, entry.tel,entry.email);

        fclose(Addressbook);

        printf("\nShtypni y/Y neqoftese doni te regjistroni person tjeter: ");
        fgets(answer, 1, stdin);
    }
    while(answer == 'y' || answer == 'Y');
}
user3484582
  • 557
  • 1
  • 6
  • 22
  • 3
    it is not the case that `fflush(stdin)` is not recommended. It is the case that it's an **error** to flush an input stream (it results in undefined behavior). Just use `fgets()` **exclusively,** and parse the string afterwards. Don't try to combine the parsing and user input phases. – The Paramagnetic Croissant May 10 '14 at 14:29
  • 2
    I support user3477950's view. I'd like to add that in general `scanf` is not considered a good way to read input for which structure is not guaranteed. It's pretty dangerous, and it is very hard or not possible at all, to read input safely. I am not sure if there is any *best* way. If you want to do it properly, you will end up with either some library, or reading everything as strings and parsing the buffers later. – luk32 May 10 '14 at 14:35
  • Sorry but I can't seem to get what parsing is. Also, how would I use `fgets()` to get an int from the user? – user3484582 May 10 '14 at 15:12
  • @user3484582 Assume line is a char buffer and x is an int. `fgets(line, sizeof(line), stdin); sscanf(line, "%d", &x);` – Brandin May 10 '14 at 15:38
  • 1
    @user3484582 In this context "parsing" means figuring out what was entered. The pseudocode should be 1. Get an input line. 2. Figure out if the line contains an integer. – Brandin May 10 '14 at 15:39
  • @Brandin I tried the `fgets()` and `sscanf()` combination but the problem persists. When I get to the first field of my struct, it automatically gets filled with empty space (though not new line). – user3484582 May 10 '14 at 16:26
  • @user3484582 You should post the code you tried along with the input. Its too hard to say just from your description. fgets and sscanf should work fine if done properly. – Brandin May 10 '14 at 16:50
  • @Brandin I added the code to the main post. Thank you for the help so far. – user3484582 May 10 '14 at 17:10
  • 1
    @user3484582 One problem I see is that the input buffer to fgets should be large enough to hold the entire line (e.g. all characters plus a '\n' plus a '\0'). Making the input buffer only 2 characters is not recommended. I would make one buffer of like 10000 characters or something. And when you get a line, copy the relevant characters into your struct – Brandin May 10 '14 at 17:29
  • You are right, increasing the size fixed it. I have another problem though. On the last call, `printf("\nShtypni y/Y neqoftese doni te regjistroni person tjeter: "); fgets(answer, 1, stdin);` I don't get prompted to input an answer. – user3484582 May 10 '14 at 18:08
  • A buffer size of 1, maybe this will help http://stackoverflow.com/questions/23388620/is-fgets-returning-null-with-a-short-bufffer-compliant (hint: do not use such small buffer sizes. YOu likely want at least 3 (1 for a `char`, 1 for the `'\n'` and 1 for the `'\0'`) – chux - Reinstate Monica Jul 02 '14 at 04:34

2 Answers2

0

With scanf, you can clear the newlines, like that. Also, I have included fgets too.

#include<stdio.h>

int main(void)
{
    int i, N = 5;
    char buffer[N];

    printf("Enter %d characters\n", N+1);

    scanf("%5s", buffer);   /* MUST check comments below on this page for this! */

    /* Clear trailing input */
    while(getchar() != '\n')
        /* discard */ ;


    for(i = 0 ; i < 5 ; ++i)
        printf("|%c|\n", buffer[i]);

    printf("End with scanf\n\n");

    /*****************************************************/

    printf("Enter %d characters\n", N+1);

    fgets(buffer, 5, stdin);

    for(i = 0 ; i < 5 ; ++i)
        printf("|%c|\n", buffer[i]);

    printf("End with fgets\n\n");

    return 0;
}

Also, this code demonstrates the limit you can put to every function for the input.

Source

gsamaras
  • 71,951
  • 46
  • 188
  • 305
0

Well "optimal" is maybe a bit subjective but I found doing a separate function to read the number makes things a bit easier and avoid scanf if you don't absolutely need it e.g.

int readNumber(int min, int max)
{
  char number[32];
  do
  {
    if ( fgets( number, sizeof(number), stdin ) != NULL )
    {
      // note that if 'number' is not a number atoi returns 0
      int n = atoi(number); 

      if ( n>= min && n <= max ) 
      { 
        return n;
      }
    }
    printf( "please enter a valid value between %d and %d\n", min, max );
  }
  while ( 1 );

  return -1; // never reached
}
AndersK
  • 35,813
  • 6
  • 60
  • 86