Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine number of characters that were read with fgets()?

Tags:

c

io

stdio

fgets

This is the description of fgets() from the man page:

char *fgets(char *s, int size, FILE *stream);
...

RETURN VALUE
  fgets() returns s on success, and NULL on error or  when  end  of  file
  occurs while no characters have been read.

It doesn't follow the pattern of read, which returns -1 on failure and the number of bytes read on success. Instead, it returns a char* which is NULL on failure and s on success. This doesn't give me any info about how long the input is. So if I have something like this:

char input_buffer[256];
fgets(input_buffer, sizeof(input_buffer), stdin);

After the fgets call, is there any way to tell how long the input is WITHOUT zero-initializing the buffer first?

Thanks.

like image 691
James Ko Avatar asked Dec 14 '17 15:12

James Ko


2 Answers

How to determine number of characters that were read with fgets()?

char *fgets(char *s, int size, FILE *stream);

Use strlen(s) after checking the fgets() return value.

if (fgets(s, size, stream)) {
  printf("number of characters that were read: %zu\n", strlen(s));
} else if (feof(stream)) {
  printf("number of characters that were read:0 End-of-file\n");
} else  {
  printf("number of characters that were read unknown due to input error\n");
}

This works unless a null character '\0' is read as strlen() will encounter that '\0' before the appended one by the function. In that case, strlen(s) after fgets() will report a smaller value.

There are various tricks to pre-fill s and then call fgets(), yet it is undefined what happens to the rest of the unread buffer. Other short comings exist.

If null characters as part of a valid input stream are a concern, use fgetc() or something like getline().


A common scenario where null characters are text is when text is encoded as UTF-16. Of course code should not use fgets() to read that text, yet that requires prior knowledge. Much code that reads text has failed in mysterious ways due to the incorrect assumption that a text file is a non-null character text file.

Further, even with a text file supposedly lacking a null characters, what happens with the following code?

if (fgets(s, size, stream)) {
  size_t len = strlen(s);
  s[--len] = '\0';  // poor way to lop off the trailing \n, this could be UB
}

Such code invokes undefined behavior with a hacker exploit: slipping a null character in the file at the beginning of the line. (See this and this for better solutions to lop off the potential \n)

Robust code does not assume the text is well formed and takes measures to detect abnormalities.


Pedantic note: there are pathological problems with fgets(char *s, int size, FILE *stream); with a size < 2.

like image 69
chux - Reinstate Monica Avatar answered Sep 21 '22 10:09

chux - Reinstate Monica


Yes there is. It is always null terminated in case of success. So it will be strlen(buf).

From standard 7.21.7.2

char *fgets(char * restrict s, int n,FILE * restrict stream); 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.

like image 34
user2736738 Avatar answered Sep 22 '22 10:09

user2736738