Fread function and files - c

I have to make a program which will print the name and the score of a student found on a text file. The text file itself will follow this format:
number of students (int)
score name (of student number one)
score name (of student number two)
So for example, the students.txt file you will see I am using for this program should be something like this:
2
45 George
23 John
The reading part of the program (for the structures) should be done using fread. I have tried what you will see below and the problem is that when I run the program the console just remains blank. Note that the program is not complete yet, since I am trying to learn how a few things work first (for example I am only trying to print the scores of the student just so I can see whether I know what I am doing) before doing it properly and I know some parts like checking for NULL return are missing. I am also wondering whether since I am using fread the txt file should be considered binary or not.
Here is what I have tried:
#include <stdio.h>
#include <stdlib.h>
#define FILENAME students.txt
#define MAX 50
typedef struct st {
float score;
char name[MAX];
} student;
void read(FILE *fp, student *p, int size);
int main()
{
int number;
student *ptr;
FILE *ifp;
ifp = fopen("FILENAME", "rb");
fscanf(ifp, "%d", &number);
ptr = (student *)malloc(number * sizeof(student));
read(ifp, ptr, number);
}
void read(FILE *fp, student *p, int size)
{
int num;
fscanf(fp,"%d", &num);
fread(p, sizeof(student), size, fp);
for(int i=0; i<size; i++)
printf("%f", p[i].score);
}

You are opening a file named "FILENAME", not "students.txt", which is why your command line is remaining blank. Change
#define FILENAME students.txt
into
#define FILENAME "students.txt"
and
fopen("FILENAME", "rb");
into
fopen(FILENAME, "r");
Furthermore, fread requires that the caller knows the exact size of the elements they are reading, but since the student names have a variable length, this is not the case. You should use fgets instead. You can read more about fgets here.
fread should only be used in the case of binary input/output, usually memory blocks (arrays or structs) that have a size known to the caller.

Related

Innacurate file readings from fopen and/or fscanf

I try to read a file from this code. I am trying to load images and store them into my program as strings, so I can later create the identical image with fprintf to a new file. I am not allowed to use some file duplication; I need to load the files in as a string and write them to a new file later. What I am attempting is to have a char array, and since one char is one byte the array is as long as the file size, and each element of the char array corresponds to one byte of the diamond block texture, and I want to also be able to write this string from the code to a new file, and have another diamond block that I can open with an image viewer.
#include <stdio.h>
#include <stdlib.h>
char Contents[468];
int main(int argc, char *argv[]) {
char *WD = getenv("HOME");
char Path[strlen(WD)+strlen("/Desktop/diamond_block.png")+1];
sprintf(Path, "%s/Desktop/diamond_block.png", WD);
FILE *File = fopen(Path, "r");
fscanf(File, "%s", Contents);
printf(Contents);
}
The result is just four letters, âPNG, and it is supposed to be hundreds of characters meaning the file is NOT being fully read. I suspect it is somehow being terminated early by some terminating character, but how can I solve my problem?
This is a very basic answer to your question. With the code below you may understand what's your issue. This code need a good review to intercept all the errors the used functions may return. By the way ... enjoy it!
The code loads the whole file fname into the char array imgMem. It compute the file dimension on the variable n, allocates the memory for the array imgMem (malloc) and then loads the whole file into imgMem (fread).
Then the code writes the first 30 bytes of the file in two format:
the hex value of the byte
the char value if the byte has a console representation (otherwise prints a .)
Here the code:
#include <unistd.h>
#include <stdio.h>
#include <malloc.h>
int main(void)
{
const char * fname = "/home/sergio/Pictures/vpn.png";
FILE * fptr;
char * imgMem=NULL;
long n;
int i;
fptr=fopen(fname, "r");
//Determine the file dimension
fseek(fptr,0,SEEK_END); n=ftell(fptr);
//Set the file cursor to the beginning
fseek(fptr,0,SEEK_SET);
printf("The file is %lu byte long.\n\n",n);
//Allocate n bytes to load the file
imgMem = malloc((size_t)n);
//Load the file
fread(imgMem,(size_t)n,1,fptr);;
for(i=0; i<30; i++) {
printf("[%02X %c] ",
(unsigned char)imgMem[i],
(imgMem[i]>31 && imgMem[i]<127)?
imgMem[i]:'.'
);
if ((i+1)%8==0)
puts("");
}
puts("");
free(imgMem);
fclose(fptr);
return 0;
}

How to make a void pointer to read a given part of a binary file

I have a binary file which contains 3 differents structs and a christmas text. On the first line of the binaryfile have they provided me with a int which represents the size of a package inside the file. A package contains 3 structs ,the chistmastext and the size.
The structs lies in a file called framehdr.h and the binary file I'm reading is called TCPdump.
Now am I trying to create a program att will read each package at a time and then withdraw the text.
I have started with something like this:
#pragma warning(disable: 4996)
#include <stdio.h>
#include <stdlib.h>
#include "framehdr.h"
#include <crtdbg.h>
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
FILE *fileOpen;
char *buffer;
size_t dataInFile;
long filesize;
// The three structs
struct ethernet_hdr ethHdr;
struct ip_hdr ipHdr;
struct tcp_hdr tcpHDr;
fileOpen = fopen("C:\\Users\\Viktor\\source\\repos\\Laboration_3\\Laboration_3\\TCPdump", "rb");
if (fileOpen == NULL)
{
printf("Error\n");
}
else
{
printf("Success\n");
}
char lenOf[10];
size_t nr;
// Reads until \n comes
fgets(lenOf, sizeof(lenOf), fileOpen);
sscanf(lenOf, "%d", &nr);
// Withdraw the size of a package and check if it's correct
printf("Value: %d\n", nr);
printf("Adress: %d\n", &nr);
void *ptr;
fread(&ptr, nr, 1, fileOpen);
int resEth = 14;
printf("resEth: %d\n", resEth);
int resIP = IP_HL((struct ip_hdr*)ptr);
printf("ResIP: %d\n", resIP);
int resTcp = TH_OFF((struct tcp_hdr*)ptr);
printf("tcpIP: %d\n", resTcp);
int res = resEth + resIP + resTcp;
printf("Total: %d", res);
fclose(fileOpen);
//free(buffer);
system("pause");
return 0;
}
I know that the first struct ethernet will always have the size of 14 but I need to get the size of the other 2 and I'm suppose to use IP_HL and TH_OFF for that.
But my problems lies in that I can't seem to read the entire package to one
void * with the fread. I get noting in my *ptr.
Which in turn makes the code break when I try to convert the void * to one of the structs ones.
What I'm doing wrong with the void *?
Two problems:
First you should not really use text functions when reading binary files. Binary files doesn't really have "lines" in the sense that text file have it.
Secondly, with
void *ptr;
fread(&ptr, nr, 1, fileOpen);
you are passing a pointer to the pointer variable, you don't actually read anything into memory and then make ptr point to that memory. What happens now is that the fread function will read nr bytes from the file, and then write it to the memory pointed to by &ptr, which will lead to undefined behavior if nr > sizeof ptr (as then the data will be written out of bounds).
You have to allocate nr bytes of memory, and then pass a pointer to the first element of that:
char data[nr];
fread(data, nr, 1, fileOpen);
You should also get into the habit of checking for errors. What if the fread function fails? Or the file is truncated and there isn't nr bytes left to read?
You can check for these conditions by checking what fread returns.
And not only check for fread, there are more functions than fopen that can fail.

How does one print a struct member after reading from binary file (using ab+) in C

Hello I am trying to write code to save a struct to a binary file then print it after reading from file. When I run my code I get a segmentation fault (11).
this is my code (sorry for all the printfs, I was trying to find where the code went wrong)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
struct contact {
int unqID;
char *name;
char *relationship;
char *phone_number;
char *email;
};
FILE *file;
file = fopen("test.bin", "ab+");
struct contact conwrite;
conwrite.unqID = 0;
conwrite.name = "president";
conwrite.relationship = "";
conwrite.phone_number = "";
conwrite.email = "";
struct contact conwrite2;
conwrite2.unqID = 1;
conwrite2.name = "vice president";
conwrite2.relationship = "";
conwrite2.phone_number = "";
conwrite2.email = "";
printf("%s\n", conwrite.name);
printf("%s\n", conwrite2.name);
fwrite(&conwrite, sizeof(conwrite), 1, file);
fwrite(&conwrite2, sizeof(conwrite2), 1, file);
fclose(file);
struct contact phonebook[100];
file = fopen("test.bin", "rb");
printf("size of file: %lu\n", sizeof(file));
printf("size of contact: %lu\n", sizeof(struct contact));
int size = (sizeof(file)) / (sizeof(struct contact));
printf("size of file: %d\n", size);
int read = fread(phonebook, sizeof(conwrite), 2, file);
printf("read: %d\n", read);
if (phonebook[0].name == NULL)
printf("phonebook is empty\n");
else
printf("phonebook is filled\n");
printf("%s\n", phonebook[0].name);
fclose(file);
return 0;
}
You cannot compute the length of the file with the sizeof operator:
printf("size of file: %lu\n", sizeof(file));
Will print the size of a FILE *, the number of byte in the pointer itself. Note that sizeof evaluates to a value of type size_t for which the proper printf format is %zu. If for some reason you cannot use the z option, cast the sizeof as (unsigned long) to use %lu but be aware that size_t is actually larger than unsigned long on some platforms such a Windows 64 bits.
To compute the length of the file on disk, you can use a system call such as stat or possibly ftell() after seeking to the end of the stream with fseek(file, 0L, SEEK_END);. Note that this method only works for tiles open in binary mode. In your program, you do open it in binary mode, but also in update (the +), which seems unnecessary.
Finally, you cannot store the information pointed to by these structures with your approach: the strings pointed to by the struct members are in memory, you are just writing the values of the pointers, which will not be meaningful when read back by a different program or even a different instance of the same program. You need to make these strings arrays of char, as for example:
struct contact {
int unqID;
char name[64];
char relationship[32];
char phone_number[16];
char email[64];
};
You will need to update your code to account for the difference in types. Note that this approach introduces a limit on the size of all textual member. Storing the information in disk files is usually not done this way. Either use specialized libraries that implement databases, or use an interchange format such as csv, json, xml... by increasing order of complexity and versatility, or a custom text based format.

Program crashes with fclose

I am working with the following text file: https://mega.nz/#!EN4iRJxA!VtKFEl9dlQHWHIzcgfOfpXTt9_ill_YkgsLWL3nORLg
And the following code:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int id;
char lastname[25];
char name[20];
float average;
}student;
int main(){
int i;
student *pointer= (student*) malloc(3);
FILE *file= fopen("Struct.txt", "r");
fread(pointer, sizeof(student), 3, file);
for(i= 0; i< 3; i++){
printf("Id: %d\nName: %s\nLast name: %s\nAverage: %.2f\n",
(pointer+ i) -> id,
(pointer+ i) -> name,
(pointer+ i) -> lastname,
(pointer+ i) -> average);
}
fclose(file);
system("pause");
return 0;
}
When I delete the fclose function it works but I checked it and seems to be correct. What am I missing?
It has nothing to do with your fclose, instead you need to fix two things:
You need to check the return error of fopen:
if( NULL == file )
{
printf( "Cannot open\n" );
exit( 1 );
}
Your malloc code doesn't look right, should be:
student *pointer= malloc(3 * sizeof(student));
Your program isn't allocating memory the way you think it is. malloc(3) returns three bytes of memory. Most likely the file pointer is immediately after it in memory
[ | | ][*FILE]
When you read in the file, the data first goes in to the three bytes of memory you've allocated. But since it doesn't fit, it keeps going and overwrites the memory containing the pointer to the file too.
[student data student data]
Then calling fclose closes the file at dat or whatever was read in from the file. This is a programming error, sometimes called a "wild pointer dereference", and if the data read in doesn't point to something that can be closed like a file can (as is happening to you) the program will crash.
The fixes suggested by artm should resolve your problem, but I thought this explanation might help you understand why you saw the crash.

typedef memory allocation

VARIABLES AREN'T SET IN STONE YET! Excuse if if no indention. I am new to this site. Anyway, I have a text document of a list of games in five different categories, and I need to some help with memory allocation VIA typedef. How would one do it? So far, this is what I have:
/*
Example of text document
2012 DotA PC 0.00 10
2011 Gran Turismo 5 PS3 60.00 12
list continues in similar fashion...
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//function prototype here
char **readFile(char *file);
char *allocateString(char temp[]);
typedef struct
{
int year;
char name[100];
char system[10];
float price;
int players;
}game;
int main(void)
{
char **list;
system("pause");
return 0;
}
//function defined here
char **readFile(char *file) //reads file and and allocates
{
FILE* fpIn;
int i, total=0;
fpIn = fopen("list.txt", "r");
if (!fpIn)
{
printf("File does not exist");
exit(101);
}
/*
allocate memory by row here VIA for loop with the total++ to keep track of the
number of games
*/
/*
allocate memory individually for each item VIA "allocateString by using going
to set list[i] = allocateStrng(tmpList) using for loop the for loop will have
for (i=0; i<total; i++)
*/
return;
}
//allocateString here
char *allocateString(char temp[]);
{
char *s;
s = (char*)calloc(strlen(temp+1), sizeof(char)));
strcpy(s, temp);
return s;
}
Usually you'd allocate a decent amount of memory up front, detect situations where that amount is not enough, and enlarge the allocation in those cases using realloc (or malloc followed by memcpy and free). This advice holds for both the buffer into which you read the current line (to be passed as temp to allocateString) and the array to hold the sequence of all lines.
You can detect an insufficient buffer size for the line buffer when after calling fgets(buf, bufsize, fpIn) the strlen(buf) == bufsize - 1 but still buf[bufsize - 2] != '\n'. In other words, when reading filled the whole buffer, but still didn't reach a newline. In that case, the next read will continue the current line. You might want an inner loop to extend the buffer and read again for as long as it takes.
Note that your allocateString pretty much duplicates strdup, so you might want to use that instead.
The links in the above text mainly come from the manual of the GNU C library. cppreference.com is another good source of C function documentation. As are the Linux man pages.
s = (char*)calloc(strlen(temp+1), sizeof(char)));
//the name of the array is a pointer, so you are doing pointer arithmetic.
//I think you want strlen(*temp+1, sizeof(char)));
// or strlen(temmp[1]) it isn't clear if this is a pointer to a string or an array
// of strings
//you need the length of the string *temp is the content which temp points to
//strcpy(s, temp);

Resources