reading from a binary file in C - c

I am currently working on a project in which I have to read from a binary file and send it through sockets and I am having a hard time trying to send the whole file.
Here is what I wrote so far:
FILE *f = fopen(line,"rt");
//size = lseek(f, 0, SEEK_END)+1;
fseek(f, 0L, SEEK_END);
int size = ftell(f);
unsigned char buffer[MSGSIZE];
FILE *file = fopen(line,"rb");
while(fgets(buffer,MSGSIZE,file)){
sprintf(r.payload,"%s",buffer);
r.len = strlen(r.payload)+1;
res = send_message(&r);
if (res < 0) {
perror("[RECEIVER] Send ACK error. Exiting.\n");
return -1;
}
}
I think it has something to do with the size of the buffer that I read into,but I don't know what it's the correct formula for it.
One more thing,is the sprintf done correctly?

If you are reading binary files, a NUL character may appear anywhere in the file.
Thus, using string functions like sprintf and strlen is a bad idea.
If you really need to use a second buffer (buffer), you could use memcpy.
You could also directly read into r.payload (if r.payload is already allocated with sufficient size).
You are looking for fread for a binary file.
The return value of fread tells you how many bytes were read into your buffer.
You may also consider to call fseek again.
See here How can I get a file's size in C?
Maybe your code could look like this:
#include <stdint.h>
#include <stdio.h>
#define MSGSIZE 512
struct r_t {
uint8_t payload[MSGSIZE];
int len;
};
int send_message(struct r_t *t);
int main() {
struct r_t r;
FILE *f = fopen("test.bin","rb");
fseek(f, 0L, SEEK_END);
size_t size = ftell(f);
fseek(f, 0L, SEEK_SET);
do {
r.len = fread(r.payload, 1, sizeof(r.payload), f);
if (r.len > 0) {
int res = send_message(&r);
if (res < 0) {
perror("[RECEIVER] Send ACK error. Exiting.\n");
fclose(f);
return -1;
}
}
} while (r.len > 0);
fclose(f);
return 0;
}

No, the sprintf is not done correctly. It is prone to buffer overflow, a very serious security problem.
I would consider sending the file as e.g. 1024-byte chunks instead of as line-by-line, so I would replace the fgets call with an fread call.
Why are you opening the file twice? Apparently to get its size, but you could open it only once and jump back to the beginning of the file. And, you're not using the size you read for anything.

Is it a binary file or a text file? fgets() assumes you are reading a text file -- it stops on a line break -- but you say it's a binary file and open it with "rb" (actually, the first time you opened it with "rt", I assume that was a typo).
IMO you should never ever use sprintf. The number of characters written to the buffer depends on the parameters that are passed in, and in this case if there is no '\0' in buffer then you cannot predict how many bytes will be copied to r.payload, and there is a very good chance you will overflow that buffer.
I think sprintf() would be the first thing to fix. Use memcpy() and you can tell it exactly how many bytes to copy.

Related

Displaying size of a file [C]

I'm making a simple sockets program to send a text file or a picture file over to another socket connected to a port. However, I want to also send the size of the file over to the client socket so that it knows how many bytes to receive.
I also want to implement something where I can send a certain number of bytes instead of the file itself. For example, if a file I wanted to send was 14,003 bytes and I felt like sending 400 bytes, then only 400 bytes would be sent.
I am implementing something like this:
#include <stdio.h>
int main(int argc, char* argv[]) {
FILE *fp;
char* file = "text.txt";
int offset = 40;
int sendSize = 5;
int fileSize = 0;
if ((fp = fopen(file, "r")) == NULL) {
printf("Error: Cannot open the file!\n");
return 1;
} else {
/* Seek from offset into the file */
//fseek(fp, 0L, SEEK_END);
fseek(fp, offset, sendSize + offset); // seek to sendSize
fileSize = ftell(fp); // get current file pointer
//fseek(fp, 0, SEEK_SET); // seek back to beginning of file
}
printf("The size is: %d", fileSize);
}
offset is pretty much going to go 40 bytes into the file and then send whatever sendSize bytes over to the other program.
I keep getting an output of 0 instead of 5. Any reason behind this?
You can try this.
#include <stdio.h>
int main(int argc, char* argv[]) {
FILE *fp;
char* file = "text.txt";
int offset = 40;
int sendSize = 5;
int fileSize = 0;
if ((fp = fopen(file, "r")) == NULL) {
printf("Error: Cannot open the file!\n");
return 1;
} else {
fseek(fp, 0L, SEEK_END);
fileSize = ftell(fp);
}
printf("The size is: %d", fileSize);
}
The fseek() to the end, then ftell() method is a reasonably portable way of getting the size of a file, but not guaranteed to be correct. It won't transparently handle newline / carriage return conversions, and as a result, the standard doesn't actually guarantee that the return from ftell() is useful for any purpose other than seeking to the same position.
The only portable way is to read the file until data runs out and keep a count of bytes. Or stat() the file using the (non-ANSI) Unix standard function.
You may be opening the file in text mode as Windows can open a file in text mode even without the "t" option.
And you can't use ftell() to get the size of a file opened in text mode. Per 7.21.9.4 The ftell function of the C Standard:
For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file
position indicator for the stream to its position at the time
of the ftell call; the difference between two such return
values is not necessarily a meaningful measure of the number of
characters written or read.
Even if it does return the "size" of the file, the translation to "text" may changed the actual number of bytes read.
It's also not portable or standard-conforming to use fseek() to find the end of a binary file. Per 7.21.9.2 The
fseek
function:
A binary stream need not meaningfully support fseek calls with a
whence value of SEEK_END.
I think your Seek does not work due to the 3rd parameter:
try to seek with
(fp, offset, SEEK_SET);
as he will try to use the number sendSize+Offset as the "origin" constant, it will be compared to the 3 constant values as below (it is 0, 1 or 2) and as nothing compares it seem to return 0 all time.
http://www.cplusplus.com/reference/cstdio/fseek/
Parameters
stream, offset, origin
Position used as reference for the offset. It is specified by one of the following constants defined in exclusively to be used as arguments for this function:
Constant Reference position
SEEK_SET Beginning of file
SEEK_CUR Current position of the file pointer
SEEK_END End of file

Proper way to get file size in C

I am working on an assignment in socket programming in which I have to send a file between sparc and linux machine. Before sending the file in char stream I have to get the file size and tell the client. Here are some of the ways I tried to get the size but I am not sure which one is the proper one.
For testing purpose, I created a file with content " test" (space + (string)test)
Method 1 - Using fseeko() and ftello()
This is a method I found on https://www.securecoding.cert.org/confluence/display/c/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file
While the fssek() has a problem of "Setting the file position indicator to end-of-file, as with fseek(file, 0, SEEK_END), has undefined behavior for a binary stream", fseeko() is said to have tackled this problem but it only works on POSIX system (which is fine because the environment I am using is sparc and linux)
fd = open(file_path, O_RDONLY);
fp = fopen(file_path, "rb");
/* Ensure that the file is a regular file */
if ((fstat(fd, &st) != 0) || (!S_ISREG(st.st_mode))) {
/* Handle error */
}
if (fseeko(fp, 0 , SEEK_END) != 0) {
/* Handle error */
}
file_size = ftello(fp);
fseeko(fp, 0, SEEK_SET);
printf("file size %zu\n", file_size);
This method works fine and get the size correctly. However, it is limited to regular files only. I tried to google the term "regular file" but I still not quite understand it thoroughly. And I do not know if this function is reliable for my project.
Method 2 - Using strlen()
Since the max. size of a file in my project is 4MB, so I can just calloc a 4MB buffer. After that, the file is read into the buffer, and I tried to use the strlen to get the file size (or more correctly the length of content). Since strlen() is portable, can I use this method instead? The code snippet is like this
fp = fopen(file_path, "rb");
fread(file_buffer, 1024*1024*4, 1, fp);
printf("strlen %zu\n", strlen(file_buffer));
This method works too and returns
strlen 8
However, I couldn't see any similar approach on the Internet using this method. So I am thinking maybe I have missed something or there are some limitations of this approach which I haven't realized.
Regular file means that it is nothing special like device, socket, pipe etc. but "normal" file.
It seems that by your task description before sending you must retrieve size of normal file.
So your way is right:
FILE* fp = fopen(...);
if(fp) {
fseek(fp, 0 , SEEK_END);
long fileSize = ftell(fp);
fseek(fp, 0 , SEEK_SET);// needed for next read from beginning of file
...
fclose(fp);
}
but you can do it without opening file:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct stat buffer;
int status;
status = stat("path to file", &buffer);
if(status == 0) {
// size of file is in member buffer.st_size;
}
OP can do it the easy way as "max. size of a file in my project is 4MB".
Rather than using strlen(), use the return value from fread(). stlen() stops on the first null character, so may report too small a value. #Sami Kuhmonen Also we do not know the data read contains any null character, so it may not be a string. Append a null character (and allocate +1) if code needs to use data as a string. But in that case, I'd expect the file needed to be open in text mode.
Note that many OS's do not even use allocated memory until it is written.
Why is malloc not "using up" the memory on my computer?
fp = fopen(file_path, "rb");
if (fp) {
#define MAX_FILE_SIZE 4194304
char *buf = malloc(MAX_FILE_SIZE);
if (buf) {
size_t numread = fread(buf, sizeof *buf, MAX_FILE_SIZE, fp);
// shrink if desired
char *tmp = realloc(buf, numread);
if (tmp) {
buf = tmp;
// Use buf with numread char
}
free(buf);
}
fclose(fp);
}
Note: Reading the entire file into memory may not be the best idea to begin with.

open_memstream with fseek to end pads buffer with zeros

I using some C code that writes binary data to a file. In the process, it seeks around to different positions and then finally seeks to the end with fseeko(fp, 0, SEEK_END);.
However, in some cases, I want to work on a stream in memory instead. I use open_memstream for this, but seeking to the end pads the buffer with zeros and it ends up being twice as big as it should be.
An example just to demonstrate the effect of the fseek to the end of the stream is below. In the actual code, we also fseek to different parts of the stream, patching and editing bits of it, etc., as the stream is processed. Note also that writing the file at the end to the filesystem is just for demonstration to show the contents of the buffer – otherwise I wouldn't need the memory stream.
#include <stdio.h>
#include <stdlib.h>
#if (defined(BSD) || __APPLE__)
#include "open_memstream.h"
#endif
int main(void) {
FILE *stream;
FILE *outfile;
char *buf;
size_t buf_len;
int i;
stream = open_memstream(&buf, &buf_len);
for(i = 0; i < 1000; i++) {
fprintf(stream, "%d\n", i);
}
fseeko(stream, 0, SEEK_END);
fclose(stream);
outfile = fopen("out.txt", "w");
fwrite(buf, buf_len, 1, outfile);
fclose(outfile);
return 0;
}
I was testing this out on Mac OS X with this implementation of open_memstream and it worked as I expected, but when I run this on Linux the file is twice the size with zeros at the end.
What's the best way to deal with this? I'm not sure if it's reliable to divide the buffer length by two and truncate it.
I've just ran into the same problem on Linux.
// It seams that SEEK_END does not work with open_memstream()
fseek(stream, 0, SEEK_END);
I've ended up doing this:
off_t o = ftell(stream);
/* do some things with the stream */
fseek(stream, o, SEEK_SET);

How to load larger text file to buffer in optimized way -- c program

I have a text file(unsigned short values) as follows
abc.txt
2311
1231
1232
54523
32423
I'm reading this file in my function using while loop and storing in a buffer as follows
while(!feof(ref))
{
fscanf(ref,"%d\n",&ref[count]);
count++;
}
It is taking too much time for reading large file is there any way to optimize the fscanf operation.
This is because secondary memory access is slower than primary memory access. First dump the file into primary memory using fread() in binary mode. Then read from primary memory integer by integer.
A common way is to read a larger chunk into a large memory buffer, and then parse out the data from that buffer.
Another way may be to instead memory map the file, then the OS will put the file into your process virtual memory map, so you can read it like reading from memory.
use a local buffer and read blocks of data using fread() in binary mode. Parse your text data and continue with the next block.
tune your buffer size properly, maybe 64K or 1Mb in size, it depends on your application.
#include <stdio.h>
int BUFFER_SIZE = 1024;
FILE *source;
FILE *destination;
int n;
int count = 0;
int written = 0;
int main()
{
unsigned char buffer[BUFFER_SIZE];
source = fopen("myfile", "rb");
if (source)
{
while (!feof(source))
{
n = fread(buffer, 1, BUFFER_SIZE, source);
count += n;
// here parse data
}
}
fclose(source);
return 0;
}
This may be faster if each line has only one number, atoi() is a lot faster than using fscanf()
#define BUFLEN 128
#define ARRAY_SIZE 12345
int myarray[ARRAY_SIZE];
char buffer[BUFLEN]
FILE *fp= fopen(...);
index=0;
do
{
if( fgets(buffer, BUFLEN-1, fp) == NULL )
break;
myarray[index++]= atoi(buffer);
if( index >= ARRAY_SIZE)
break;
}while(!feof(fp));
...hastily typed in code, not compiled or run ;)
You can improve the file reading by setting a stream buffer e.g.
#define STRMBUF_SIZE (64*1024)
char strmbuf[STRMBUF_SIZE];
setvbuf( fp, strmbuf,_IOFBF,STRMBUF_SIZE);

C: can't write data on file

i want to open a file, write some data on it so i have to use (Fopen) " i can't use open because i need fopen in some other things "
now if i want to write on the file using fwrite it just don't i don't know why this is what i referred to in my code #option1, but if i get the file descriptor and use normal write method everything works fine see #option 2 below.
anyone can help me to make fwrite works ?
char file_data[256] // has some values
int file_size = strlen(file_data);
FILE *file;
file = fopen(MY_FILE_NAME, "w+");
if(!file){//edited
return false;
}
#option 1//this is not working
fwrite(file_data,1,file_size,file);
#end of option 1
#option 2//this works
int fd = fileno(file);
int x = write(fd,file_data,file_size);//
#end of option 1
EDIT
my file_data is something like this
4 bytes is reserved for an integer (required)
200 bytes is reserved for a string (optional)
buffered IO operations use a buffer that is managed by the C lib. Your "problem" is that fwrite is buffered meaning that in order to write to the file you most likely need to flush it with fflush() or just close the file.
First of all:
if(!file < 0 ){
return false;
}
file is either NULL (on failure) or not (on success) - there's no point in testing it against 0 as it's a pointer (therefore, unsigned).
Your fwrite call seems OK, but you should make sure that the amount you're trying to write is correct (is there a null-terminated string inside file_data?).
Another problem you may be facing is that you don't close or flush the file - this may cause some data to remain in the file-buffer and not be written to the disk.
If you want to check the fopen() return value, do like this:
if (file == NULL) return false;
then, if you want to write a string fputs() is preferable, IMHO, because it communicates better that what you're writing is a string.
Since, according to your last edit, you aren't writing ASCII strings, this is what you should code:
#include <stdio.h>
struct String
{
int size;
char data[200];
};
int main()
{
struct String s;
FILE* file = NULL;
file = fopen("filename", "wb+");
memset(&s, '\0', sizeof(s));
strcpy(s.data, "Hello, world!");
s.size = strlen(s.data);
fwrite(&s, 1, sizeof(s), file);
if (!file) return 1;
fclose(file);
}
At first sight, the mistake seems to be at line #2:
int file_size = strlen(file_data);
This only works if there exists a terminal nul character. So file_size must be either provided for example as a function argument or the you must use the full size of the array.
The following should work:
int write_in_my_file(int data_int, const char* data_str)
{
size_t written;
FILE* file = fopen(MY_FILE_NAME, "wb+"); /* SuperJulietta */
if (!file) return false;
written = fwrite(&data_int, sizeof(data_int), 1, file);
if (written == sizeof(data_int))
{
if (opt_str) fputs(opt_str, file);
}
fclose(file);
return written == sizeof(data_int);
}
Note: this code was not compiled, and error handling is partial.
Edit : if you don't close the file, you'll have to call fflush instead.
You have to put a fflush(file); after the fwrite to force the writing of the data or you can also remove the buffer completely by doing a setbuf(file, NULL); after your fopen call.
I think you need to either do fclose(file) or fflush(file). because fopen is buffered IO so It does not write immidiately, so to ensure that file write is done, you need to do this.
I guess your fwrite code is not the problem.
Whenever the first byte in your file_data is \0 then you write nothing. Since the data is not a string, write 256 bytes. This code works:
#include <stdio.h>
#include <string.h>
#define MY_FILE_NAME "sample.bin"
#define SAMPLE_DATA "Content Content"
int main()
{
char file_data[256];
int file_size = sizeof(file_data);
// fill in some sample data
memcpy(file_data, SAMPLE_DATA, sizeof(SAMPLE_DATA));
FILE *file = fopen(MY_FILE_NAME, "w+");
if (file) {
fwrite(file_data, 1, file_size, file);
fclose(file);
}
}
You see, this is your fwrite. I use sizeof instead of strlen to determine the amount of bytes that will be written...
BR
fwrite is used for binary output, so you have to open file with "wb"

Resources