5

What is the difference between fgets() and gets()?

I am trying break my loop when the user hits just "enter". It's working well with gets(), but I don't want to use gets(). I tried with fgets() and scanf() but I don't have the same results as with gets(). fgets() breaks the loop whatever user enters in text! Here is my code :

void enter(void)
{
  int i,

  for(i=top; i<MAX; i++)
    {
      printf(".> Enter name (ENTER to quit): ");
      gets(cat[i].name);

      if(!*cat[i].name)
         break;

      printf(".> Enter Last Name: ");
      scanf("%s",cat[i].lastname);
      printf(".> Enter Phone Number: ");
      scanf("%s",cat[i].phonenum);
      printf(".> Enter e-Mail: ");
      scanf("%s",cat[i].info.mail);
      printf(".> Enter Address: ");
      scanf("%s",cat[i].info.address);
      printf("\n");
    }
  top = i;
}
Makyen
  • 31,849
  • 12
  • 86
  • 121
pureofpure
  • 1,060
  • 2
  • 12
  • 31
  • 1
    Why don't you want to use `gets`? – Cilan Feb 08 '15 at 22:20
  • i am working on linux , and its throw me warning .. i dont know , its fine to use gets? – pureofpure Feb 08 '15 at 22:21
  • Oh yeah, I remember it can create a buffer overflow. Could you explain something about why you don't want to use `gets` in your question, please? – Cilan Feb 08 '15 at 22:23
  • gets is dangerous http://stackoverflow.com/questions/1694036/why-is-the-gets-function-dangerous-why-should-it-not-be-used – Milind Dumbare Feb 08 '15 at 22:24
  • @Milind Bleh, I'm new to C. I'm sorry if anyone wants to kill me now. – Cilan Feb 08 '15 at 22:25
  • no one wants to kill you. we just want clarity in the question. given that neither gets() nor fgets() is controlling the loop, some of use are a bit confused by your statement that fgets() cause a break out of the loop. – user3629249 Feb 08 '15 at 23:11
  • what is this line: 'if(!*cat[i].name)' expected to be doing? – user3629249 Feb 08 '15 at 23:14

4 Answers4

4

A difference between gets() and fgets() is that fgets() leaves the newline in the buffer. So instead of checking whether the first element of the input is 0, check whether it's '\n';

fgets(cat[i].name, sizeof cat[i].name, stdin);
if (cat[i].name[0] == '\n' || cat[i].name[0] == 0) {
    // empty line or no input at all
    break;
} else {
    // remove the trailing newline
    int len = strlen(cat[i].name);
    cat[i].name[len-1] = 0;
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • ok i just try this. BUT its break the loop , seems like its reading from buffer some text or something .. – pureofpure Feb 08 '15 at 22:34
  • When you use `scanf` to read the address, it doesn't consume the newline at the end of the line. So the call to `fgets()` when the loop repeats will read that newline. – Barmar Feb 08 '15 at 22:40
  • Ok, so how can i fix this ? – pureofpure Feb 08 '15 at 22:45
  • The best solution is not to use `scanf`. Read each line with `fgets`, and use `sscanf` to parse the line. – Barmar Feb 08 '15 at 22:48
  • Ok i will try to read each line with fgets , its possible to make any function with fgets in it , to not use everytime all this check ? – pureofpure Feb 08 '15 at 22:51
  • You could write your own function that calls `fgets` and removes the newline. – Barmar Feb 08 '15 at 22:53
3

Drop gets() and scanf().
Create a helper function to handle and qualify user input.

// Helper function that strips off _potential_ \n
char *read1line(const char * prompt, char *dest, sizeof size) {
  fputs(prompt, stdout);
  char buf[100];
  *dest = '\0';
  if (fgets(buf, sizeof buf, stdin) == NULL) {
    return NULL;  // EOF or I/O error
  }
  // Remove potential \n
  size_t len = strlen(buf);
  if (len > 0 && buf[len-1] == '\n') {
    buf[--len] = `\0`;
  }
  // Line is empty or too long
  if (len == 0 || len >= size) return NULL;
  return memcpy(dest, buf, len+1);
}

void enter(void)
{
  int i;

  for(i=top; i<MAX; i++)
    {
      if (read1line(".> Enter name (ENTER to quit): ", 
          cat[i].name, sizeof cat[i].name) == NULL) break;
      if (read1line(".> Enter Last Name: ", 
          cat[i].lastname, sizeof cat[i].lastname) == NULL) break;
      if (read1line(".> Enter Phone Number: ", 
          cat[i].phonenum, sizeof cat[i].phonenum) == NULL) break;
      if (read1line(".> Enter e-Mail: ", 
          cat[i].info.mail, sizeof cat[i].info.mail) == NULL) break;
      if (read1line(".> Enter Address: ", 
          cat[i].info.address, sizeof cat[i].info.address) == NULL) break;
    }
  top = i;
}

Some attributes of fgets() and gets():

fgets() reads input and saves to a buffer until:
1) The buffer is 1 shy of being full - or -
2) '\n' is encountered - or -
3) The stream reaches an end-of-file condition - or -
4) An input error occurs.

gets() does #2 - #4 above except it scans, but does not save a '\n'.
gets() is depreciated in C99 and no longer part of C11.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • WOW! that was awesome, i did my job with this , exept the function parameter sizeof size , i change it to int size , because i had error ! thanks – pureofpure Feb 09 '15 at 01:00
0

The problematic difference between gets and fgets is that gets removes the trailing '\n' from an input line but fgets keeps it.

This means an 'empty' line returned by fgets will actually be the string "\n".

The nasty difference, that means it's best to avoid gets altogether, is that if you give gets a line that's too long your program will crash in very bad ways.

user3710044
  • 2,261
  • 15
  • 15
0

you can use fgets() with STDIN instead. This function is secured and always insert a '\0' at the string end.

An example:

char inputbuffer[10];
char *p;
p = fgets(inputbuffer, sizeof(inputbuffer), stdin);
printf(">%s<\n", p);    /* p is NULL on error, but printf is fair */

You'll get at most 9 characters + '\0', in this example.

levif
  • 2,156
  • 16
  • 14