Does fgets() always terminate the char buffer with \0?
Asked Answered
N

5

22

Does fgets() always terminate the char buffer with \0 even if EOF is already reached? It looks like it does (it certainly does in the implementation presented in the ANSI K&R book), but I thought I would ask to be sure.

I guess this question applies to other similar functions such as gets().

EDIT: I know that \0 is appended during "normal" circumstances, my question is targeted at EOF or error conditions. For example:

FILE *fp;
char b[128];
/* ... */
if (feof(fp)) {
    /* is \0 appended after EACH of these calls? */
    fgets(b, 128, fp);
    fgets(b, 128, fp);
    fgets(b, 128, fp);
}
Nessim answered 2/11, 2009 at 9:27 Comment(2)
Always check the return value of fgets(): if (fgets(...) == NULL) /* indeterminate array */;Calcic
You may be right, however, I see there are other answers stating \0 is always appended, even if fgets() returns NULL.Nessim
C
8

Never use gets!!

    7.19.7.2 The fgets function
    Synopsis
1           #include <stdio.h>
            char *fgets(char * restrict s, int n,
                 FILE * restrict stream);
    Description
2   The fgets function reads at most one less than the number of characters
    specified by n from the stream pointed to by stream into the array pointed
    to by s. No additional characters are read after a new-line character
    (which is retained) or after end-of-file. A null character is written
    immediately after the last character read into the array.
    Returns
3   The fgets function returns s if successful. If end-of-file is encountered
    and no characters have been read into the array, the contents of the array
    remain unchanged and a null pointer is returned. If a read error occurs
    during the operation, the array contents are indeterminate and a null
    pointer is returned.

So, yes, when fgets() does not return NULL the destination array always has a null character.

If fgets() returns NULL, the destination array may have been changed and may not have a null character. Never rely on the array after getting NULL from fgets().


Edit example added

$ cat fgets_error.c
#include <stdio.h>

void print_buf(char *buf, size_t len) {
  int k;
  printf("%02X", buf[0]);
  for (k=1; k<len; k++) printf(" %02X", buf[k]);
}

int main(void) {
  char buf[3] = {1, 1, 1};
  char *r;

  printf("Enter CTRL+D: ");
  fflush(stdout);
  r = fgets(buf, sizeof buf, stdin);
  printf("\nfgets returned %p, buf has [", (void*)r);
  print_buf(buf, sizeof buf);
  printf("]\n");

  return 0;
}
$ ./a.out
Enter CTRL+D:
fgets returned (nil), buf has [01 01 01]
$

See? no NUL in buf :)

Calcic answered 2/11, 2009 at 9:42 Comment(3)
@Calcic re "Never use gets!!", do you have an alternative for file reading?Dichasium
@atlex2: yes, the alternative is fgets().Calcic
getline is better alternative, if you are ok limiting yourself to recent POSIX-like systems such as Linux and OSX (or having your own version for platforms where it is missing).Wardell
D
17

fgets does always add a '\0' to the read buffer, it reads at most size - 1 characters from the stream (size being the second parameter) because of this.

Never use gets as you can never guarantee that it won't overflow any buffer that you give it, so while it technically does always terminate the read string this doesn't actually help.

Dictator answered 2/11, 2009 at 9:38 Comment(0)
C
8

Never use gets!!

    7.19.7.2 The fgets function
    Synopsis
1           #include <stdio.h>
            char *fgets(char * restrict s, int n,
                 FILE * restrict stream);
    Description
2   The fgets function reads at most one less than the number of characters
    specified by n from the stream pointed to by stream into the array pointed
    to by s. No additional characters are read after a new-line character
    (which is retained) or after end-of-file. A null character is written
    immediately after the last character read into the array.
    Returns
3   The fgets function returns s if successful. If end-of-file is encountered
    and no characters have been read into the array, the contents of the array
    remain unchanged and a null pointer is returned. If a read error occurs
    during the operation, the array contents are indeterminate and a null
    pointer is returned.

So, yes, when fgets() does not return NULL the destination array always has a null character.

If fgets() returns NULL, the destination array may have been changed and may not have a null character. Never rely on the array after getting NULL from fgets().


Edit example added

$ cat fgets_error.c
#include <stdio.h>

void print_buf(char *buf, size_t len) {
  int k;
  printf("%02X", buf[0]);
  for (k=1; k<len; k++) printf(" %02X", buf[k]);
}

int main(void) {
  char buf[3] = {1, 1, 1};
  char *r;

  printf("Enter CTRL+D: ");
  fflush(stdout);
  r = fgets(buf, sizeof buf, stdin);
  printf("\nfgets returned %p, buf has [", (void*)r);
  print_buf(buf, sizeof buf);
  printf("]\n");

  return 0;
}
$ ./a.out
Enter CTRL+D:
fgets returned (nil), buf has [01 01 01]
$

See? no NUL in buf :)

Calcic answered 2/11, 2009 at 9:42 Comment(3)
@Calcic re "Never use gets!!", do you have an alternative for file reading?Dichasium
@atlex2: yes, the alternative is fgets().Calcic
getline is better alternative, if you are ok limiting yourself to recent POSIX-like systems such as Linux and OSX (or having your own version for platforms where it is missing).Wardell
H
4

man fgets:

fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a new‐line is read, it is stored into the buffer. A '\0' is stored after the last character in the buffer.

Hanahanae answered 2/11, 2009 at 9:42 Comment(0)
C
2

If you did open the file in binary mode "rb", and if you want to read Text line by line by using fgets you can use the following code to protect your software of loosing text, if by a mistake the text contained a '\0' byte. But finally like the others mentioned, normally you should not use fgets if the stream contains '\0'.


size_t filepos=ftell(stream);
fgets(buffer, buffersize, stream);
len=strlen(buffer);
/* now check for > len+1 since no problem if the 
   last byte is 0 */
if(ftell(stream)-filepos > len+1) 
{
    if(!len) filepos++;
    if(!fseek(stream, filepos, SEEK_SET) && len)
    {
        fread(buffer, 1, len, stream);
        buffer[len]='\0';
    }
}
Cedilla answered 15/8, 2015 at 11:54 Comment(0)
L
1

Yes it does. From CPlusPlus.com

Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the End-of-File is reached, whichever comes first.

A newline character makes fgets stop reading, but it is considered a valid character and therefore it is included in the string copied to str.

A terminating null character is automatically appended after the characters copied to str.

Lurleen answered 2/11, 2009 at 9:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.