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);
}
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.
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 :)
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.
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';
}
}
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 a 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 null character is automatically appended in str after the characters read to signal the end of the C string.
Related
I have the following code:
int i = 0;
char ar[500];
while(i < 20)
{
printf("Type a line: ");
fgets(ar, 500, stdin);
fprintf(fp,"%s", ar);
i++;
}
Basically, i am trying to open a file, then take 20 lines of input from the user and store it in an array, then print it to the file. It works fine but i don't understand how fgets works in the while loop. As i understand, when i = 0, fgets stores the 1st line to ar[500] and fprintf prints it out to the file, when i = 1, fgets stores the 2nd line to ar[500], but now ar[500] has 2 lines, but why fprintf only prints the 2nd line to the file but not all the 2 lines and so on when i increment by 1.
fgets always start populating the array at arr[0]. At every iteration, the previous line is overwritten. fgets will add the null terminating character for you in arr, so only the currently read line will be outputed to FILE pointed to by fp.
The description of char *fgets(char * restrict s, int n, FILE * restrict stream);
is (C11 7.21.7p2):
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.
The first character read by fgets will be always stored at s[0] and so forth up until at most s[n - 1] or the first newline character encountered (it being the last character stored). The element after the last character read will be set to '\0', therefore terminating the string.
I.e. effectively a fgets call will overwrite k + 1 first elements in the array where k is either the length of the line or n, whichever is smaller, and 1 means the null termination.
To store 20 lines, you'd need an array of arrays:
char ar[20][500];
then read into ar[i].
The buffer ar will be overwritten each time.
fgets has the prototype char *fgets(char *str, int n, FILE *stream). It will read a maximum of n-1 characters from stream and write them to str and then adds the '\0' character.
regarding: "but now ar[500] has 2 lines,"
NO, the function fgets() does NOT append.
Rather, it overlays.
In the posted code, always starting at ar[0]
Is this safe to do? Does fgets terminate the buffer with null or should I be setting the 20th byte to null after the call to fgets and before I call clean?
// strip new lines
void clean(char *data)
{
while (*data)
{
if (*data == '\n' || *data == '\r') *data = '\0';
data++;
}
}
// for this, assume that the file contains 1 line no longer than 19 bytes
// buffer is freed elsewhere
char *load_latest_info(char *file)
{
FILE *f;
char *buffer = (char*) malloc(20);
if (f = fopen(file, "r"))
if (fgets(buffer, 20, f))
{
clean(buffer);
return buffer;
}
free(buffer);
return NULL;
}
Yes fgets() always properly null-terminates the buffer. From the man page:
The fgets() function reads at most one less than the number of characters specified by n from the given stream and stores them in the string s. Reading stops when
a newline character is found, at end-of-file or error. The newline, if any, is retained. If any characters are read and there is no error, a '\0' character is
appended to end the string.
If there is an error, fgets() may or may not store any zero bytes anywhere in the buffer. Code which doesn't check the return value of fgets() won't be safe unless it ensures there's a zero in the buffer somewhere; the easiest way to do that is to unconditionally store a zero to the last spot. Doing that will mean that an unnoticed error may (depending upon implementation) cause a bogus extra line of data to be read, but won't fall off into Undefined Behavior.
I am trying to get the input of one character using fgets(). To my knowledge fgets will addend the \n to the end of the input unless there is no room.
char test[1];
fgets(test,1,stdin);
readRestOfLine();
while (strcmp(test,"z") != 0){
......
......
}
Anyway the loop is never run even when z is entered. Why is this?
man fgets
char *fgets(char *s, int size, FILE *stream);
fgets() reads in at most one less than size characters from stream and
stores them into the buffer pointed to by s...
A terminating null byte ('\0') is stored after the last character in
the buffer.
In your case of size 1 this means fgets() reads in zero, i. e. no, characters and stores the terminating '\0' in test[0].
strcmp operates on strings as follows from it's name, so test has to be \0 terminated string. test must have room for \0.
As you stated correctly fgets appends a '\n' at the end of the string. So if you input just "z", then the resulting string will be "z\n" which is not equal to "z".
Furthermore the size of the buffer for fgets is only on character long in your program, but this length must be at least as long as your longest string you intend to enter.
Try this:
char test[100]; // space for 98 characters + \n + terminating zéro
fgets(test, 100, stdin);
readRestOfLine();
while (strcmp(test,"z\n") != 0){
......
......
}
While I could use strings, I would like to understand why this small example I'm working on behaves in this way, and how can I fix it ?
int ReadInput() {
char buffer [5];
printf("Number: ");
fgets(buffer,5,stdin);
return atoi(buffer);
}
void RunClient() {
int number;
int i = 5;
while (i != 0) {
number = ReadInput();
printf("Number is: %d\n",number);
i--;
}
}
This should, in theory or at least in my head, let me read 5 numbers from input (albeit overwriting them).
However this is not the case, it reads 0, no matter what.
I understand printf puts a \0 null terminator ... but I still think I should be able to either read the first number, not just have it by default 0. And I don't understand why the rest of the numbers are OK (not all 0).
CLARIFICATION: I can only read 4/5 numbers, first is always 0.
EDIT:
I've tested and it seems that this was causing the problem:
main.cpp
scanf("%s",&cmd);
if (strcmp(cmd, "client") == 0 || strcmp(cmd, "Client") == 0)
RunClient();
somehow.
EDIT:
Here is the code if someone wishes to compile. I still don't know how to fix
http://pastebin.com/8t8j63vj
FINAL EDIT:
Could not get rid of the error. Decided to simply add #ReadInput
int ReadInput(BOOL check) {
...
if (check)
printf ("Number: ");
...
# RunClient()
void RunClient() {
...
ReadInput(FALSE); // a pseudo - buffer flush. Not really but I ignore
while (...) { // line with garbage data
number = ReadInput(TRUE);
...
}
And call it a day.
fgets reads the input as well as the newline character. So when you input a number, it's like: 123\n.
atoi doesn't report errors when the conversion fails.
Remove the newline character from the buffer:
buf[5];
size_t length = strlen(buffer);
buffer[length - 1]=0;
Then use strtol to convert the string into number which provides better error detection when the conversion fails.
char * fgets ( char * str, int num, FILE * stream );
Get string from stream.
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 happens first.
A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str. (This means that you carry \n)
A terminating null character is automatically appended after the characters copied to str.
Notice that fgets is quite different from gets: not only fgets accepts a stream argument, but also allows to specify the maximum size of str and includes in the string any ending newline character.
PD: Try to have a larger buffer.
Is this safe to do? Does fgets terminate the buffer with null or should I be setting the 20th byte to null after the call to fgets and before I call clean?
// strip new lines
void clean(char *data)
{
while (*data)
{
if (*data == '\n' || *data == '\r') *data = '\0';
data++;
}
}
// for this, assume that the file contains 1 line no longer than 19 bytes
// buffer is freed elsewhere
char *load_latest_info(char *file)
{
FILE *f;
char *buffer = (char*) malloc(20);
if (f = fopen(file, "r"))
if (fgets(buffer, 20, f))
{
clean(buffer);
return buffer;
}
free(buffer);
return NULL;
}
Yes fgets() always properly null-terminates the buffer. From the man page:
The fgets() function reads at most one less than the number of characters specified by n from the given stream and stores them in the string s. Reading stops when
a newline character is found, at end-of-file or error. The newline, if any, is retained. If any characters are read and there is no error, a '\0' character is
appended to end the string.
If there is an error, fgets() may or may not store any zero bytes anywhere in the buffer. Code which doesn't check the return value of fgets() won't be safe unless it ensures there's a zero in the buffer somewhere; the easiest way to do that is to unconditionally store a zero to the last spot. Doing that will mean that an unnoticed error may (depending upon implementation) cause a bogus extra line of data to be read, but won't fall off into Undefined Behavior.