Does fgets() hold somehow where it stopped reading from a FILE *? - c

I am trying to get a sample (shell script) program on how to write to a file:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv){
char buff[1024];
size_t len, idx;
ssize_t wcnt;
for (;;){
if (fgets(buff,sizeof(buff),stdin) == NULL)
return 0;
idx = 0;
len = strlen(buff);
do {
wcnt = write(1,buff + idx, len - idx);
if (wcnt == -1){ /* error */
perror("write");
return 1;
}
idx += wcnt;
} while (idx < len);
}
}
So my problem is this: Let's say I want to write a file of 20000 bytes so every time I can only write (at most) 1024 (buffer size).
Let's say that in my first attempt everything is going perfect and fgets() reads 1024 bytes and in my first do while I write 1024 bytes.
Then, since we wrote "len" bytes we exit the do-while loop.
So now what?? The buffer is full from our previous reading. It seems to me that for some reason it is implied that fgets() will now continue reading from the point it reached in in-file the last time. (buf[1024] here).
How come, fgets() knows where it stopped reading in the in-file?
I checked the man page :
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 newline is read, it is stored in the buffer. A terminating null byte (aq\0aq) is stored after the last character in the buffer.
fgets() return s on success, and NULL on error or when the end of file occurs while no characters have been read.*
So from that, I get that it returns a pointer to the first element of buf, which is always buf[0],
that's why I am confused.

When using aFILE stream, it contains information about the position in the file (among other things). fgets and other functions like freador fwrite merely utilize this information and updates it when an operation is performed.
So, whenever fgets reads from the stream, the stream will be updated to maintain the position, so that the next operation starts off where the previous ended.

Related

Difference between specifications of fread and fgets?

What is the difference between fread and fgets when reading in from a file?
I use the same fwrite statement, however when I use fgets to read in a .txt file it works as intended, but when I use fread() it does not.
I've switched from fgets/fputs to fread/fwrite when reading from and to a file. I've used fopen(rb/wb) to read in binary rather than standard characters. I understand that fread will get /0 Null bytes as well rather than just single lines.
//while (fgets(buff,1023,fpinput) != NULL) //read in from file
while (fread(buff, 1, 1023, fpinput) != 0) // read from file
I expect to read in from a file to a buffer, put the buffer in shared memory, and then have another process read from shared memory and write to a new file.
When I use fgets() it works as intended with .txt files, but when using fread it adds a single line from 300~ characters into the buffer with a new line. Can't for the life of me figure out why.
fgets will stop when encountering a newline. fread does not. So fgets is typically only useful for text files, while fread can be used for both text and binary files.
From the C11 standard:
7.21.7.2 The fgets function
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.
7.21.8.1 The fread function
The fread function reads, into the array pointed to by ptr, up to nmemb elements whose size is specified by size, from the stream pointed to by stream. For each object, size calls are made to the fgetc function and the results stored, in the order read, in an array of unsigned char exactly overlaying the object. The file position indicator for the stream (if defined) is advanced by the number of characters successfully read. If an error occurs, the resulting value of the file position indicator for the stream is indeterminate. If a partial element is read, its value is indeterminate.
This snippet maybe will make things clearer for you. It just copies a file in chunks.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
if(argc != 3) {
printf("Usage: ./a.out src dst\n");
printf("Copies file src to dst\n");
exit(EXIT_SUCCESS);
}
const size_t chunk_size = 1024;
FILE *in, *out;
if(! (in = fopen(argv[1], "rb"))) exit(EXIT_FAILURE);
if(! (out = fopen(argv[2], "wb"))) exit(EXIT_FAILURE);
char * buffer;
if(! (buffer = malloc(chunk_size))) exit(EXIT_FAILURE);
size_t bytes_read;
do {
// fread returns the number of successfully read elements
bytes_read = fread(buffer, 1, chunk_size, in);
/* Insert any modifications you may */
/* want to do here */
// write bytes_read bytes from buffer to output file
if(fwrite(buffer, 1, bytes_read, out) != bytes_read) exit(EXIT_FAILURE);
// When we read less than chunk_size we are either done or an error has
// occured. This error is not handled in this program.
} while(bytes_read == chunk_size);
free(buffer);
fclose(out);
fclose(in);
}
You mentioned in a comment below that you wanted to use this for byteswapping. Well, you can just use the following snippet. Just insert it where indicated in code above.
for(int i=0; i < bytes_read - bytes_read%2; i+=2) {
char tmp = buffer[i];
buffer[i] = buffer[i+1];
buffer[i+1] = tmp;
}

memset() after while(fgets)

My program is supposed to read from stdin and hand over the input to the system - in case the input does not equal "exit".
That works perfectly unless the second input is longer than the first.
For example if the first input is "hello" and the second is "hellohello", the input gets split up into "hello" and "ello".
I guess the problem is that the buffer s is not properly cleared while looping. Therefore I used memset() but unfortunately I did not get the results I was looking for.
Can anyone see the mistake?
Thanks a lot!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 1024
int main(){
char *s = (char *)malloc(MAX);
char *temp = NULL;
while(fgets(s, (sizeof(s)-1), stdin)!= NULL){
temp = s+(strlen(s)-1);
*temp = '\0';
if (strcmp(s,"exit")==0){
break;
} else {
system(s);
}
memset(s, 0, MAX);
}
free(s);
return 0;
}
The incorrect thing here is (sizeof(s)-1). This will not return size of allocated buffer, instead return size of (char*). You size of buffer is MAX. memset() really doesn't do anything with this, so remove it. an you do not need to do that -1, fgets() will always automatically attach zero terminator in the end of string, even if buffer filled up.
Also these two lines
temp = s+(strlen(s)-1);
*temp = '\0';
are not needed, because
"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 newline is read, it is stored into the buffer. A terminating null byte (aq\0aq) is stored after the last character in the buffer."
(from "man fgets", google for it)

Unistd read() maximum size

In the following snippet, no matter how long of an input I put in (EDIT: I'm copy and pasting in a random string), say a string with 9998 characters, read() stops when i = 4095. It states it read in an EOF character, but my string does not have an EOF character (for example I tried a string of 9998 'a's). The return value also suggests there is no error from read(). Why does read() only read in only 4095 bytes?
#include <unistd.h>
#include <stdio.h>
int main() {
char temp;
char buf[10000];
int i = 0;
while(read(STDIN_FILENO, &temp, 1) > 0) {
buf[i] = temp;
i++;
}
printf("%d\n", i);
}
Edit: To clarify, read() doesn't literally state that it read in an EOF character, per https://linux.die.net/man/2/read read() returns 0 when it moves past the EOF.
You're most likely seeing the terminal buffer limit -- terminals can only read a limited number of characters on a single line, and if you type in more than that (or simulate typing with a pseudo-terminal or cut-n-paste) without entering an NL, EOL, or EOL2 character, you'll get an error, which the terminal indicates with an EOF (read returning 0).
You can generally avoid this problem by putting the terminal into non-canonical mode (where it doesn't try to buffer lines to allow backspacing).

fgets doesnt get the last character

for some reason, when I print phrase in the following code I discover that the fgets function doesn't get the last characater in the text file. I already checked mone1 and saw that its given enough space for the text in the file. Does someone have an explanation and a soloution to that occurrence?
Tnx, Dean.
p.s I'm pretty sure the length of the string is not a problem cause even when I'm changing it to 2 characters it's still prints only the first(doesn't print the last char), and it's all written in the same line.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
printf("ss");
FILE * sent=NULL;
FILE * voca=NULL;
sent=fopen(argv[1],"r");
voca=fopen(argv[2],"r");
if(voca==NULL){
printf("cannot open voca ");
fclose(voca);
}
if(sent==NULL){
printf("cannot open sent");
fclose(sent);
}
int mone1=0;
int mone2=0;
while(EOF!=fgetc(sent))
mone1++;
while(EOF!=fgetc(voca))
mone2++;
fseek(sent,0L,SEEK_SET);
fseek(voca,0L,SEEK_SET);
char* phrase=(char*)(malloc(mone1*sizeof(char)));
char* voc=(char*)(malloc(mone2*sizeof(char)));
fgets(phrase,mone1,sent);
fgets(voc,mone2,voca);
printf("%s",phrase);
return 0;
}
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.
In another word, if you need to read mone1 characters, pass in n as at least mone1 + 1 and make sure the buffer is enough, too. The reason is fgets will add the trailing \0 at the end.
fgets() reads size-1 characters from the file stream and adds Null character at the last.
For example
fgets(buf,2,file_stream);
buf[0]=some_character and buf[1]='\0';

Why does an output string show data I never wrote?

I'm writing a program that encrypts a file by adding 10 to each character. Somehow a portion of the programs working directory is being printed to the file, and I have no idea why.
#include <stdio.h>
int main(void){
FILE *fp;
fp=fopen("tester.csv","r+");
Encrypt(fp);
fclose(fp);
}
int Encrypt(FILE *fp){
int offset=10;
Shift(fp, offset);
}
int Decrypt(FILE *fp){
int offset= -10;
Shift(fp, offset);
}
int Shift(FILE *fp, int offset){
char line[50],tmp[50], character;
long position;
int i;
position = ftell(fp);
while(fgets(line,50,fp) != NULL){
for(i=0;i<50;i++){
character = line[i];
character = (offset+character)%256;
tmp[i] = character;
if(character=='\n' || character == 0){break;}
}
fseek(fp,position,SEEK_SET);
fputs(tmp,fp);
position = ftell(fp);
fseek(stdin,0,SEEK_END);
}
}
the file originally reads
this, is, a, test
i, hope, it, works!
after the program is run:
~rs}6*s}6*k6*~o}~
/alexio/D~6*y|u}+
k6*~o}~
/alexio/D
where users/alexio/Desktop is part of the path. How does this happen???
Because you "encode" the string, it won't be null terminated (that's your case), or it will contain a null even before the end of the string (character+offset % 256 == 0). Later you try to write it as a string, which overruns your buffer, and outputs part of your program arguments.
Use fread and fwrite.
The line
fputs(tmp,fp);
writes out a probably non-null terminated string. So it continues to copy memory to the file until it finds a null.
You need to add a null to the end of 'tmp' in the case where the loop breaks on a newline.
A number of things:
You're encoding all 50 chars from your read buffer, regardless of how many were actually read with fgets(). Recall that fgets() reads a line, not an entire buffer (unless the line is longer than a buffer, and your's is not). Anything past the string length from your line file input is stack garbage.
You're then dumping all that extra garbage data, andbeyond, by not terminating your tmp[] string before writing with fputs() which you should not be using anyway. Yet-more stack garbage.
Solution. Use fread() and fwrite() for this encoding. There is no reason to be using string functions whatsoever. When you write your decoder you'll thank yourself for using fread() and fwrite()

Resources