C - How fgets works with mutiple calls - c

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]

Related

Proper length passed to fgets

If I have the following buffer:
char buffy[40]; // includes \0 at the end
Should the fgets function have STLEN 40 or 39? Why?
char buffy[40];
while (fgets(buffy, 40, fp) != EOF)
// ...
The number of characters that fgets reads in is at most one less than the value of its second parameter. So the proper value should be 40 in this context. It will read at most 39 characters and the last element of the array will be used to store the '\0'. This ensures that no buffer overrun will occur. As an idiom
fgets(buf, sizeof buf, fp)
may be used if (and only if) the declaration of the array buf is visible.
Notice that there is nothing wrong with calling it as fgets(buf, 39, fp), but this will cause it to read in at most 38 characters, and the '\0' will be stored in buf[38] (if 38 characters have been read). The last element of the array (buf[39]) will not be used at all.
Per C11 7.21.7.2 The fgets function:
Synopsis
#include <stdio.h>
char *fgets(char * restrict s, int n,
FILE * restrict stream);
Description
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.
So
char buffy[40];
while (fgets(buffy, 40, fp) != NULL)
is correct, but
char buffy[40];
while (fgets(buffy, sizeof(buffy), fp) != NULL)
would be better.
If you will write for example
fgets(buffy, 39, fp)
then the function fgets will know nothing about that actually the array contains 40 characters.:)
So the 40-th element of the array will not be used and will be redundant.
As you declared the array with 40 characters
char buffy[40];
then use all of them in a call of fgets as for example
fgets(buffy, sizeof( buffy ), fp)
The function will read from the string no more than 39 characters and appends the array with the zero character '\0'.
If you need to remove the new line character '\n' from the inputted string then you can write for example
buffy[ strcspn( buffy, "\n" ) ] = '\0';
As stated in the fgets docs:
Arguments:
...
n - This is the maximum number of characters to be read (including the final null-character). Usually, the length of the array passed as str is used.
So, you should use 40 if your array has a length of 40.

Indexing in arrays when using fgets

I'm teaching myself programming. I read that a string stored in an array of characters can be indexed to extract the nth character.
However, I've been trying to solve this for hours: I realized trying to solve an exercise that I can only access the first character of the array myarray[0]; whereas the rest of index (1,2,3...) values will return nothing. However, if I use the puts function it does return the whole string. Curious thing: strlen is returning the length of my array +1.
example:
int main (void)
{
char myarray[1000]={0};
int i;
fgets(myarray,1000,stdin);
for(i=0;i<strlen(myarray);i++)
printf("myarray[%d]:%c\n",i,myarray[i]);
printf("\n");
printf("strlen:%d\n",strlen(myarray));
puts(myarray);
return 0;
}
input:
6536
output:
strlen:5
myarray[0]:6
myarray[1]:
myarray[2]:
myarray[3]:
myarray[4]:
6536
You are getting this result most probably because of undefined behavior of your program. You are using wrong format specifier to print a size_t type (strlenreturn size_t type). Change the format specifier to %zu.
Also note that in for loop you need to declare i as size_t type.
Here is the fixed code: http://ideone.com/0sMadV
fgets writes a newline character \n into the buffer to represent newlines in the input stream. Thus strlen returns 5.
The actual output is :
6536
myarray[0]:6
myarray[1]:5
myarray[2]:3
myarray[3]:6
myarray[4]:
strlen:5
6536
As you can see, myarray[4] stores the newline (due to fgets). Also, it would be better to calculate strlen once by placing it above the loop, instead of in every iteration.
From here:
char *fgets(char *restrict s, int n, FILE *restrict stream);
The fgets() function shall read bytes from stream into the array
pointed to by s, until n-1 bytes are read, or a is read and
transferred to s, or an end-of-file condition is encountered. The
string is then terminated with a null byte.
A simple way is to for strlen(var)-1. Another way is to remove the newline with null terminating character:
if (myarray[length - 1] == '\n')
myarray[length - 1] = '\0';
where length = strlen(myarray);
The strlen counts the '\n' at the end of the string. You can "fix" it with strtok:
strtok(myarray, "\n");

Reading input with fgets?

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){
......
......
}

I don't understand the behavior of fgets in this example

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.

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

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.

Resources