How does FILE stream buffer work? - c

I understand fopen() opens file and creates a buffer for read and write operations on that file. fopen() returns a pointer for that buffer.
So my question is, in the code below, the _copy function body has a temp matrix to transfer between the fread() and fwrite(). why cant I directly transfer from buffer to buffer?
/* example: copyfile.exe xxxxx.txt zzzzzz.txt */
#include <stdio.h>
#include <stdlib.h>
#define BUFF 8192
void _copy(FILE *source, FILE *destination);
int main(int argc, char *argv[])
{
FILE *fp1, *fp2; // fp1 source file pointer// fp2 copied file pointer
if (argc !=3 ) //command line must have 3 arguments
{
fprintf(stderr, "Usage: %s (source file) (copy file)\n", argv[0][0]);
exit(EXIT_FAILURE);
}
if ((fp1 = fopen(argv[1], "rb")) == NULL) //Opening source file
{
fprintf(stderr, "Could not open %s\n",argv[1]);
exit(EXIT_FAILURE);
}
if((fp2 = fopen(argv[2], "ab+")) == NULL) //Opening destination file
{
fprintf(stderr, "could not create %s \n",argv[2]);
exit(EXIT_FAILURE);
}
if( setvbuf(fp1,NULL, _IOFBF, BUFF) != 0) //Setting buffer for source file
{
fputs("Can't create output buffer\n", stderr);
exit(EXIT_FAILURE);
}
if( setvbuf(fp2,NULL, _IOFBF, BUFF) != 0) //Setting buffer for destination file
{
fputs("Can't create input buffer\n", stderr);
exit(EXIT_FAILURE);
}
_copy(fp1, fp2);
if (ferror(fp1)!=0)
fprintf(stderr, "Error reading file %s\n", argv[1]);
if(ferror(fp2)!=0)
fprintf(stderr, "Error writing file %s\n",argv[2]);
printf("Done coping %s (source) to %s (destination) \n",argv[1], argv[2]);
fclose(fp1);
fclose(fp2);
return (0);
}
void _copy(FILE *source, FILE *destination)
{
size_t bytes;
static char temp[BUFF];
while((bytes = fread(temp,sizeof(char),BUFF,source))>0)
fwrite(temp,sizeof(char),bytes,destination);
}

You cannot use the underlying buffer from a FILE * in another FILE *. As you were told in comment, FILE * is an opaque pointer. But you can avoid the overhead of copying data between buffers by forcing both files in non buffered mode:
setbuf(fp, NULL); // cause the stream to be unbuffered

Related

Reading a pdf file with fread in C does not end up as expected

I am trying to read from a pdf file and write into another file where I run to the problem.
In the while loop, fread reads only 589 bytes which is expected to be 1024 for the first time.
In the second loop, fread reads 0 bytes.
I am sure that the pdf file is beyond 1024 bytes.
Here is a similar problem. The phenomenon is the same. But I do not use strlen() which causes that problem.
So how can I resolve the problem?
My code is here:
#include <stdio.h>
#define MAXLINE 1024
int main() {
FILE *fp;
int read_len;
char buf2[MAXLINE];
FILE *fp2;
fp2 = fopen("test.pdf", "w");
if ((fp = fopen("LearningSpark.pdf", "r")) == NULL) {
printf("Open file failed\n");
}
while ((read_len = fread(buf2, sizeof(char), MAXLINE, fp)) > 0) {
int write_length = fwrite(buf2, sizeof(char), read_len, fp2);
if (write_length < read_len) {
printf("File write failed\n");
break;
}
}
return 0;
}
fopen(filename, "r") is system dependent. See this post on what may happen to the data you read if you are on Windows, for example. Basically it is related to how certain characters are translated on different systems in text mode, ie., \n is "End-of-Line" on Unix-type systems, but on Windows it is \r\n.
Important: On Windows, ASCII char 27 will result in End-Of-File, if reading in text mode, "r", causing the fread() to terminate prematurely.
To read a binary file, use the "rb" specifier. Similarly for "w", as mentioned here, you should use "wb" to write binary data.
Binary files such as pdf files must be open in binary mode to prevent end of line translation and other text mode handling on legacy systems such as Windows.
Also note that you should abort when fopen() fails and you should close the files.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define MAXLINE 1024
int main() {
char buf2[MAXLINE];
int read_len;
FILE *fp;
FILE *fp2;
if ((fp = fopen("LearningSpark.pdf", "rb")) == NULL) {
fprintf(stderr, "Open file failed for %s: %s\n", "LearningSpark.pdf", strerror(errno));
return 1;
}
if ((fp2 = fopen("test.pdf", "wb")) == NULL) {
fprintf(stderr, "Open file failed for %s: %s\n", "test.pdf", strerror(errno));
fclose(fp);
return 1;
}
while ((read_len = fread(buf2, 1, MAXLINE, fp)) > 0) {
int write_length = fwrite(buf2, 1, read_len, fp2);
if (write_length < read_len) {
fprintf(stderr, "File write failed: %s\n", strerror(errno));
break;
}
}
fclose(fp);
fclose(fp2);
return 0;
}

C programming, copying from one file to another using command line arguments

This is my code
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc < 4) {
printf("Missing arguments\n");
return -1;
}
// Check if buffer is valid before reading anything
int bufferSize = atoi(argv[3]);
if (!bufferSize || bufferSize < 1) {
printf("Invalid buffer size\n");
return -1;
}
printf("*** Copying from '%s' to '%s' (Buffer size: %dB) ***\n",
argv[1], argv[2], bufferSize);
// READ SOURCE FILE
FILE *inputFile = fopen(argv[1], "r");
if (!inputFile) {
printf("Error opening source file\n");
return -1;
}
// READ DESTINATION FILE
FILE *outputFile = fopen(argv[2], "w");
if (!outputFile) {
printf("Error opening destination file\n");
return -1;
}
int buffer[bufferSize];
int bytes;
do {
bytes = fread(buffer, 1, bufferSize, inputFile);
if (fwrite(buffer, 1, bytes, outputFile) != bytes) {
printf("Error writing into destination file\n");
return -1;
}
} while (bytes > 0);
fclose(inputFile);
fclose(outputFile);
return 0;
}
But when I try to exe the file it doesn't work. What could be the problem?
Here's the command line:
/Users/jurajc/Documents/Program/C/L1\ 1/C_program/c_program file.txt fileCopy.txt 512
*** Copying from 'file.txt' to 'fileCopy.txt' (Buffer size: 512B) ***
Error opening source file
The input file file.txt cannot be opened: either because it is not present in the current directory or because you do not have read access to it.
You should output more informative error messages. Note also these problems:
if (!bufferSize || bufferSize < 1) is a redundant test. if (bufferSize < 1) is sufficient.
the error messages should be output to stderr
the files should be open in binary mode to reliably copy all file types on legacy systems.
the read/write loop is incorrect: you should stop when fread returns 0 before attempting to write 0 elements to the output file.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc < 4) {
fprintf(stderr, "Missing arguments\n");
return -1;
}
// Check if buffer is valid before reading anything
int bufferSize = atoi(argv[3]);
if (bufferSize < 1) {
fprintf(stderr, "Invalid buffer size: %s\n", argv[3]);
return -1;
}
printf("*** Copying from '%s' to '%s' (Buffer size: %dB) ***\n",
argv[1], argv[2], bufferSize);
// READ SOURCE FILE
FILE *inputFile = fopen(argv[1], "rb");
if (!inputFile) {
fprintf(stderr, "Error opening source file %s: %s\n",
argv[1], strerror(errno));
return -1;
}
// READ DESTINATION FILE
FILE *outputFile = fopen(argv[2], "wb");
if (!outputFile) {
fprintf(stderr, "Error opening destination file %s: %s\n",
argv[2], strerror(errno));
return -1;
}
int buffer[bufferSize];
int bytes;
while ((bytes = fread(buffer, 1, bufferSize, inputFile)) != 0) {
if (fwrite(buffer, 1, bytes, outputFile) != bytes) {
fprintf(stderr, "Error writing into destination file: %s\n", strerror(errno));
return -1;
}
}
fclose(inputFile);
fclose(outputFile);
return 0;
}

Storing .raw File Data as a Pointer Using C

I am attempting to read a '.raw' file which stores the contents of an image that was taken on a camera using C. I would like to store these contents into a uint16_t *.
In the following code I attempt to store this data into a pointer, using fread(), and then write this data into a test file, using fwrite(), to check if my data was correct.
However, when I write the file back it is completely black when I check it.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define MAX_ROW 2560
#define MAX_COL 2160
int main()
{
char filename[32] = "image1.raw";
FILE * image_raw = fopen(filename, "rb");
fseek(image_raw, 0, 2);
long filesize = ftell(image_raw);
/*READ IMAGE DATA*/
uint16_t * image_data_ptr;
image_data_ptr = (uint16_t *)malloc(sizeof(uint16_t)*MAX_ROW*MAX_COL);
fread(image_data_ptr, sizeof(uint16_t), filesize, image_raw);
fclose(image_raw);
/*TEST WRITING THE SAME DATA BACK INTO TEST RAW FILE*/
FILE *fp;
fp = fopen("TEST.raw", "w");
fwrite(image_data_ptr, sizeof(uint16_t), filesize, fp);
fclose(fp);
return 0;
}
There are multiple issues with your code:
lack of error handling.
not seeking the input file back to offset 0 after seeking it to get its size. Consider using stat() or equivalent to get the file size without having to seek the file at all.
not dividing filesize by sizeof(uint16_t) when reading from the input file, or writing to the output file. filesize is expressed in bytes, but fread/fwrite are expressed in number of items of a given size instead, and your items are not 1 byte in size.
not opening the output file in binary mode.
leaking the buffer you allocate.
With that said, try something more like this instead:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main()
{
char filename[32] = "image1.raw";
FILE *image_raw = fopen(filename, "rb");
if (!image_raw) {
fprintf(stderr, "Can't open input file\n");
return -1;
}
if (fseek(image_raw, 0, SEEK_END) != 0) {
fprintf(stderr, "Can't seek input file\n");
fclose(image_raw);
return -1;
}
long filesize = ftell(image_raw);
if (filesize == -1L) {
fprintf(stderr, "Can't get input file size\n");
fclose(image_raw);
return -1;
}
rewind(image_raw);
long numSamples = filesize / sizeof(uint16_t);
/*READ IMAGE DATA*/
uint16_t *image_data_ptr = (uint16_t*) malloc(filesize);
if (!image_data_ptr) {
fprintf(stderr, "Can't allocate memory\n");
fclose(image_raw);
return -1;
}
size_t numRead = fread(image_data_ptr, sizeof(uint16_t), numSamples, image_raw);
if (numRead != numSamples) {
fprintf(stderr, "Can't read samples from file\n");
free(image_data_ptr);
fclose(image_raw);
return -1;
}
fclose(image_raw);
/*TEST WRITING THE SAME DATA BACK INTO TEST RAW FILE*/
FILE *fp = fopen("TEST.raw", "wb");
if (!fp) {
fprintf(stderr, "Can't open output file\n");
free(image_data_ptr);
return -1;
}
if (fwrite(image_data_ptr, sizeof(uint16_t), numSamples, fp) != numSamples) {
fprintf(stderr, "Can't write to output file\n");
fclose(fp);
free(image_data_ptr);
return -1;
}
fclose(fp);
free(image_data_ptr);
return 0;
}
You have already a great answer and useful comments
anyway, consider that if you want to iterate over your file, loaded in memory as a whole, as an array of unsigned words:
if the file size could be odd what to do at the last byte/word
you may read the file as a whole in a single call, after having the file size determined
fstat() is the normal way to get the file size
get the file name from the command line as an argument is much more flexible than recompile the program or change the file name in order to use the program
The code below does just that:
uses image.raw as a default for the file name, but allowing you to enter the file name on the command line
uses fstat() to get the file size
uses a single fread() call to read the entire file as a single record
A test using the original program file as input:
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 20/07/2021 17:40 1067 main.c
PS > gcc -Wall -o tst main.c
PS > ./tst main.c
File is "main.c". Size is 1067 bytes
File "main.c" loaded in memory.
PS > ./tst xys
File is "xys". Could not open: No such file or directory
The C example
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char**argv)
{
const char* default_file = "image.raw";
char f_name[256];
if (argc < 2)
strcpy(f_name, default_file);
else
strcpy(f_name, argv[1]);
FILE* F = fopen(f_name, "rb");
if (F == NULL)
{
printf("File is \"%s\". ", f_name);
perror("Could not open");
return -1;
}
struct stat info;
fstat(_fileno(F),&info);
printf("File is \"%s\". Size is %lu bytes\n", f_name, info.st_size);
uint16_t* image = malloc(info.st_size);
if (image == NULL)
{ perror("malloc() error");
return -2;
};
if (fread(image, info.st_size, 1, F) != 1)
{ perror("read error");
free(image);
return -3;
};
// use 'image'
printf("File \"%s\" loaded in memory.\n", f_name);
free(image);
fclose(F);
return 0;
}

I am trying to print a txt file and it doesn't work in C homework

I'm writing code that's supposed to verify that a .txt file is a certain format.
I wrote my code as I saw in a tutorial and in the website
and for some reason my program doesn't even print my file.
Can you tell me what I'm doing wrong?
The code will do something far more complex, but I'm still trying to work on my basics.
Here's my code so far:
int main(int argc, char *argv[]) {
/* argv[0] = name of my running file
* argv[1] = the first file that i receive
*/
define MAXBUFLEN 4096
char source[MAXBUFLEN + 1];
int badReturnValue = 1;
char *error = "Error! trying to open the file ";
if (argc != 2) {
printf("please supply a file \n");
return badReturnValue;
}
char *fileName = argv[1];
FILE *fp = fopen(argv[1], "r"); /* "r" = open for reading */
if (fp != NULL) {
size_t newLen = fread(&source, sizeof(char), MAXBUFLEN, fp);
if (ferror(fp) != 0) {
printf("%s %s", error, fileName);
return badReturnValue;
}
int symbol;
while ((symbol = getc(fp)) != EOF) {
putchar(symbol);
}
printf("finish");
fclose(fp);
}
else {
printf("%s %s", error, fileName);
return badReturnValue;
}
}
I think you need a bit more explanations:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// there might be a macro BUFLEN defined in stdio
// which size is optimized for reading in chunks.
// Test if avaiable otherwise define it
#ifndef BUFLEN
# define BUFLEN 4096
#endif
int main(int argc, char *argv[])
{
char source[BUFLEN];
char *filename;
FILE *fp;
size_t fpread, written;
char c;
int ret_fclose;
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
// reset errno, just in case
errno = 0;
// work on copy
filename = malloc(strlen(argv[1]) + 1);
if (filename == NULL) {
fprintf(stderr, "Allocating %zu bytes failed\n", strlen(argv[1]) + 1);
exit(EXIT_FAILURE);
}
filename = strcpy(filename, argv[1]);
// try to open the file at 'filename'
fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Opening file \"%s\" filename failed\n", filename);
// errno might got set to something usable, check and print
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
// You have two options here. One is to read in chunks of MAXBUFLEN
while ((fpread = fread(&source, 1, BUFLEN, fp)) > 0) {
// Do something with the stuff we read into "source"
// we do nothing with it here, we just write to stdout
written = fwrite(&source, 1, fpread, stdout);
// you can use 'written' for error check when writing to an actual file
// but it is unlikely (but not impossible!) with stdout
// test if we wrote what we read
if ((fpread - written) != 0) {
fprintf(stderr, "We did not write what we read. Diff: %d\n",
(int) (fpread - written));
}
}
// fread() does not distinguish between EOF and error, we have to check by hand
if (feof(fp)) {
// we have read all, exit
puts("\n\n\tfinish\n");
// No, wait, we want to do it again in a different way, so: no exit
// exit(EXIT_SUCCESS);
} else {
// some error may have occured, check
if (ferror(fp)) {
fprintf(stderr, "Something bad happend while reading \"%s\"\n", filename);
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
}
// the other way is to read it byte by byte
// reset the filepointers/errors et al.
rewind(fp);
// rewind() should have reseted errno, but better be safe than sorry
errno = 0;
printf("\n\n\tread and print \"%s\" again\n\n\n\n", filename);
// read one byte and print it until end of file
while ((c = fgetc(fp)) != EOF) {
// just print. Gathering them into "source" is left as an exercise
fputc(c, stdout);
}
// clean up
errno = 0;
ret_fclose = fclose(fp);
// even fclose() might fail
if (ret_fclose == EOF) {
fprintf(stderr, "Something bad happend while closing \"%s\"\n", filename);
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
// The macros EXIT_FAILURE and EXIT_SUCCESS are set to the correct values for
// the OS to tell it if we had an eror or not.
// Using exit() is noot necessary here but there exits teh function atexit()
// that runs a given function (e.g: clean up, safe content etc.) when called
exit(EXIT_SUCCESS);
}
You read from the file twice but only print once.
If the file is to small the first reading will read all of the contents, and the second reading will not produce anything so you don't print anything.
I believe you have to reset the pointer after using fread.
Try fseek(fp, SEEK_SET, 0) to reset the pointer to the beginning of the file. Then print the file.

How to implement a FIFO log file?

I am trying to use a FIFO file to store buffered log entries for my application if the network connection is lost. I thought when reading from a FIFO file it would be deleted completely thus it would be "popped" from the log queue.
However, my approach kept logging reading the same text indefinitely. Perhaps it would be easier to read all entries into a buffer and try to log them to the database and write back all entries that couldn't be logged.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/**
* First argument is the filename
* Second argument is the log message
* Third argument is the severity
*/
int main(int argc, char **argv) {
int err, severity;
char c;
char *fifoName = "/tmp/bufferedlog";
char src[100], msg[100];
FILE *fp;
/**
* if the program has exactly three arguments it will push
* a new log entry to the queue.
*/
if(argc == 3) {
fp = fopen(fifoName, "w");
if(fp == NULL) {
err = mknod(fifoName, S_IFIFO | 0600, 0);
if(err < 0){
fprintf(stderr, "Unable to create fifo: %s\n",fifoName);
exit(EXIT_FAILURE);
}
fp = fopen(fifoName, "w");
if(fp == NULL) {
fprintf(stderr, "Unable to open fifo for writing\n");
exit(EXIT_FAILURE);
}
}
fprintf(fp, "\"%s\" \"%s\" %d ","My Application", argv[1], atoi(argv[2]));
fflush(fp);
fclose(fp);
return 0;
}
fp = fopen(fifoName, "r");
if(fp == NULL) {
fprintf(stderr, "Unable to open fifo for reading\n");
exit(EXIT_FAILURE);
}
/**
* it will now try to read the log entry from the fifo file.
*/
while(1) {
if(fscanf(fp,"\"%[^\"]\" \"%[^\"]\" %d", src, msg, &severity) < 0) { // ignore the overflow bugs
goto finish;
}
fprintf(stdout, "Do you really want to log \"%s (%d):%s\" to database [y/n]?\n", src, severity, msg);
scanf(" %c",&c);
if(c=='y') {
/* logs to database here */
/* if it fails to log to database it will break out of this loop */
fprintf(stdout, "Pushed log entry to database.\n");
}else {
goto finish;
}
}
fclose(fp);
fp = fopen(fifoName, "w");
/**
* if the database connection failed it fill put the log entry back into
* the queue and try again when the program starts next time.
*/
if(fp == NULL) {
fprintf(stderr, "Unable to open fifo for writing\n");
exit(EXIT_FAILURE);
}
fprintf(fp, "\"%s\" \"%s\" %d ", src, msg, severity);
fflush(fp);
finish:
fclose(fp);
return 0;
}
Please ignore the overflow bugs with scanf. My attempt doesn't work, here is the psuedocode of my program.
if argc is equal to three then
write log entry to fifo file
end
while there is data in fifo file do
read log entry from fifo file and delete it from fifo file
try logging entry to database
if failed then
break out of while loop
end
end
write log entry back to fifo file

Resources