fread() not working correctly? - c

I have this code that is meant to read from a text file, store the information in a bin file and then read the information from the bin file and project it onto the screen.
I have the writing to the bin file all correct except when I go to print the tempStudents to the screen it always says the LAST option that is in the text file. So it's as if the only student it's saving is the last student.
#include<stdlib.h>
#include<stdio.h>
struct student {
char name[200];
float marks;
};
int main() {
FILE * txtFile;
struct student tempStudent;
// File pointer to binary file
FILE * binFile;
int searchNum;
if ((txtFile = fopen("/Users/Ash/Desktop/Lab 8B/input.txt", "r")) == NULL) {
printf("Can not open file input.txt\n");
}
else {
FILE * binFile;
binFile = fopen("/Users/Ash/Desktop/Lab 8B/binFile.bin","w+b");
while (fscanf(txtFile,"%s %f", tempStudent.name, &(tempStudent.marks)) == 2) {
fwrite(tempStudent.name,sizeof(char),sizeof(tempStudent.name),binFile);
fwrite(&tempStudent.marks,sizeof(int),1,binFile);
}
printf("Please enter the student you want to search for\n");
printf("For example if you want the first student type 1\n");
scanf("%d", &searchNum);
int i = 0;
for (i = 0; i <= searchNum; i++)
{
fread(tempStudent.name, 60, sizeof(char),binFile);
fread(&tempStudent.marks,60, sizeof(int),binFile);
}
// write code that reads in the student structure that the user asked for
// from the binary file and store it in the variable tempStudent
printf("The student name retreived is: %s\n", tempStudent.name);
printf("The student mark retreived is: %.2f\n", tempStudent.marks);
fclose(binFile);
fclose(txtFile);
}
return 0;
}

A file has something like a current position. After writing the binary data to the file this position is at the end. When you (try to) read the bin data in this state fread will read nothing.
Check the return values!

You are always searching one too far:
for (i = 0; i <= searchNum; i++)
if you want the first student (searchNum = 1), then you will in fact do two reads. Usually this gets you "one more than the value I intended to read".
More critically, if you are reading and writing from a file, you need to make sure you start at the right place. For this you have the fseek() function. Instead of doing lots of reads / writes, just make sure you are in the right place before reading or writing.
More importantly still, you seem to have variable length records for your name and marks - this makes the whole thing a giant mess. Some recommendations:
Make the record length constant - in this way you will be able to fseek to a particular record without having to read all the previous records first.
Be VERY CAREFUL about reading and writing to a file at the same time; consider first writing all the inputs to file, closing the file, then opening for reading
Make sure you read the right number of bytes... don't just hard wire "60".
Learn about fseek(). Google it.

You are writing 60*sizeof(int) bytes of data into a single float element:
struct student {
char name[200];
float marks;
};
struct student tempStudent;
fread(&tempStudent.marks,60,sizeof(int),binFile);
Surely you can't expect this code to work!!!

Related

Modify an existing line in file and trim the rest of the line

Please help me, I made a function to modify a line in file using C language. Now my problem is that although the line is modified, the rest of the line I modified is still as is; For example if the new data I enter is shorter than the original line, my new data will be written, but the rest of the line will still be filled with old data. Please check the first line in the outputs below. Please advise me, thank you very much.
Original file:
Sam,Thomas,10-06-1995,26 Elhoreya Street,01234567899,sthomas#gmail.com
Steven,Thomas,10-06-1995,26 Elhoreya Street,01234567899,sthomas#gmail.com
Expected output file:
John,George,12-1-2000,23 MN Street,1234567899,gJoh#gmail.com
Steven,Thomas,10-06-1995,26 Elhoreya Street,01234567899,sthomas#gmail.com
Real output file:
John,George,12-1-2000,23 MN Street,1234567899,gJoh#gmail.com#gmail.com
Steven,Thomas,10-06-1995,26 Elhoreya Street,01234567899,sthomas#gmail.com
This is my function:
void modify(contact c[])
{
int flag=0, k=0, count_matches=0, match_cont_pos[100], choose;
char name[50], temp;
FILE * file;
file = fopen("info.txt", "rb+");
if(file == NULL)
{
printf("Error opening file\n");
exit(1);
}
else
{
printf("\nEnter the name you want to search: ");
scanf("%s",&name);
for(int j = 0; name[j] != '\0'; j++)
name[j] = tolower(name[j]);
for(int i = 0; i < count ; i++) {
for(int j = 0; c[i].last_name[j] != '\0'; j++)
c[i].last_name[j] = tolower(c[i].last_name[j]);
if(strcmp(c[i].last_name,name) == 0)
{
match_cont_pos[k]=i;
k++;
}
}
for(int d=0;d<k;d++)
printf("match pos %d\n",match_cont_pos[d]);
for(int d=0;d<k;d++){
printf("\n%s\n%s\n%d %d %d\n%s\n%d\n%s\n\n", c[match_cont_pos[d]].last_name,c[match_cont_pos[d]].first_name,c[match_cont_pos[d]].DoB.day,c[match_cont_pos[d]].DoB.month,c[match_cont_pos[d]].DoB.year,c[match_cont_pos[d]].street_address,c[match_cont_pos[d]].phone_number,c[match_cont_pos[d]].email);
}
if(k>1){
printf("Enter the number of the record you want to modify: ");
scanf("%d",choose);
printf("Enter last name: ");
scanf("%s",&c[match_cont_pos[choose-1]].last_name);
printf("Enter first name: ");
scanf("%s",&c[match_cont_pos[choose-1]].first_name);
printf("Enter phone number: ");
scanf("%d",&c[match_cont_pos[choose-1]].phone_number);
printf("Enter email: ");
scanf("%s",&c[match_cont_pos[choose-1]].email);
printf("Enter birth date: ");
scanf("%d %d %d",&c[match_cont_pos[choose-1]].DoB.day,&c[match_cont_pos[choose-1]].DoB.month,&c[match_cont_pos[choose-1]].DoB.year);
printf("Enter the address: ");
scanf("%c",&temp); // temp statement to clear buffer
scanf("%[^\n]",&c[match_cont_pos[choose-1]].street_address);
fseek(file,-sizeof(c),SEEK_SET);
fprintf(file,"%s,%s,%d-%d-%d,%s,%d,%s",c[match_cont_pos[choose-1]].last_name,c[match_cont_pos[choose-1]].first_name,c[match_cont_pos[choose-1]].DoB.day,c[match_cont_pos[choose-1]].DoB.month,c[match_cont_pos[choose-1]].DoB.year,c[match_cont_pos[choose-1]].street_address,c[match_cont_pos[choose-1]].phone_number,c[match_cont_pos[choose-1]].email);
}
}
fclose(file);
}
Consider these suggestions, they are to lead you towards an answer:
Your file is like a byte array, modifying some bytes in the middle does not change the position of the remaining bytes in the file.
You may be overwriting part of one line and leaving extra characters from the old line in place if the new line is shorter than the old one.
You might be overwriting part of the next line if the new line is longer than the old line
The code searches the 'C[]' array and not the file, is the array and file guaranteed to be in sync? Are you searching the array or the file?
The line " for(int i = 0; i < count ; i++)" has a variable 'Count'. Where is 'Count' defined?
Can you please advice me one last thing, can you tell me a way to move along the lines in the file? because fseek() is moving by letters, is there other ways to move through the lines?
There's no shortcut with an ordinary text file [without some organization].
It's [effectively] just a long string of bytes. If your file was (e.g):
Brown,John,123 Elm St\n
FogginFoodle,Cynthia;36277 North Hollywood Avenue\n
Mertz,Fred,7003 Fifth Ave Apt 6\n
There's no way to "seek by newline". We have to read sequentially.
But ...
There are some approximate tricks.
For example, we could read the file sequentially once, and have an array of offsets of where the newlines are.
If we only write/modify/update occasionally, we could open the file for readonly, and mmap the [entire] file and use fast string functions (e.g. strchr) to find newlines within the mapped area of main memory.
See my answers:
Copying the lines from a file to another backwards in c
read line by line in the most efficient way *platform specific*
How does mmap improve file reading speed?
The following is pretty much a waste unless we're using the mmap technique.
If the file is large enough [to make this complexity worthwhile], and is sorted by some field (e.g.) last name ...
We have/get the length of the file. We can do a binary search [sort of].
Go to the midpoint position. This is just changing a char * pointer that is within the mapped memory area.
The [rough] midpoint we chose, may be [probably will be] in the middle of a line. So, just go char-by-char forward/backward [again, by incrementing/decrementing the char * pointer], stopping on the next/previous newline. Now, we can look at the field just after a given newline. This will be the last name.
We can compare against our "desired" last name, and adjust the midpoint of the binary search up/down. We repeat the above steps until we find the record we're looking for.
Unless we have a huge amount of data, this may be overkill. And, if we do have a large amount of data, we probably want the "fixed record size" approach I mentioned in my top comment (i.e.) we have a database of sorts.
It is very fast for scanning and record update.
It suffers from the same issue that the text file had if we're trying to insert/delete records. We'd have to [as Jonathan would say] "fiddle". But, with mmap approach the fiddling is just a memcpy in conjunction with ftruncate [and possible mmap/mremap]. But, we're still having to copy and we need to issue lock to prevent others from accessing the file while we're changing it.
That's why companies make money on selling database software ;-)
A simpler technique may be to split up the large file. Create a directory. Put all records that have a last name starting with "A" in the A file in the directory, "B" in B, ...

How to read particular data from file using fread?

Following code writes data of student into a file using fwrite and reads data using fread:
struct record
{
char name[20];
int roll;
float marks;
}student;
#include<stdio.h>
void main()
{
int i;
FILE *fp;
fp=fopen("1.txt","wb"); //opening file in wb to write into file
if(fp==NULL) //check if can be open
{
printf("\nERROR IN OPENING FILE");
exit(1);
}
for(i=0;i<2;i++)
{
printf("ENTER NAME, ROLL_ NO AND MARKS OF STUDENT\n");
scanf("%s %d %f",student.name,&student.roll,&student.marks);
fwrite(&student,sizeof(student),1,fp); //writing into file
}
fclose(fp);
fp=fopen("1.txt","rb"); //opening file in rb mode to read particular data
if(fp==NULL) //check if file can be open
{
printf("\nERROR IN OPENING FILE");
exit(1);
}
while(fread(&student.marks,sizeof(student.marks),1,fp)==1) //using return value of fread to repeat loop
printf("\nMARKS: %f",student.marks);
fclose(fp);
}
As you can see in output image, marks with some other values are also printed whereas for desired output marks only with with value 91 and 94 are required
Which corrections are needed to be done in the above code to get desired output?
You are reading and writing records of different lengths, and thus your reads are giving you empty floating point numbers. If you write your records as three segments of a structure, you must read back the entire length of the structure to locate the fields you are interested in.
while(fread(&student, sizeof(student), 1, fp) == 1)) //using return value of fread to repeat loop
printf("\nMARKS: %f",student.marks);
Doing fread operations of sizeof(student.marks) number of bytes at a time may give you spurious results, given how you did fwrite operations on sizeof(student) numbers of bytes.
Another way to think about this is to pretend you're a book publisher. You print or write a book onto one piece of paper at a time. When you want to go back and find the page number on each page, you wouldn't read the pages one word at a time. That will give you a weird/wrong answer. You read in the whole page to get back the page number you want.
Investigate fread-ing sizeof(student) number of bytes on each iteration, writing those bytes into a student struct. Then access the marks property of that struct.

Need help reading from a file in C

I have been looking around for a solution but cannot seem to find a solution to my question so I will ask it. I am working in C and am reading in a .txt and taking all the values and storing them in an array then doing various tasks with them. Now my problem is that no matter what I do I cannot get file pointer I create to point to the file for some reason. I have done this for projects in the past and have compared my code then to the current one and cannot see the issue. The filename needs to be read in from the command line as well. I think there is something wrong with what I'm passing through the command line but am not sure. I have stepped through and the filename is being passed correctly but when it tries to open I get a null pointer so there is just something I'm missing.
The text file will contain a series of numbers, the first number will be the number of numbers in the file after that first number. (So if the number is 10 then there will be ten numbers after 10 is read in) after that first number the remaining numbers will be 0-9 in a random order.
Below is my current chunk of code only involving reading of the file and storing its data. (I already know the array will be of size 10 which is why the array is declared with that size.)
int main(int argc, char *argv[])
{
char* filename = "numbers.txt";
int arr[10];
int numElem;
int indexDesired = 0;
FILE *fp;
fp = fopen(filename, "r"); // open file begin reading
if (!fp)
{
printf("The required file parameter name is missing\n");
system("pause");
exit(EXIT_FAILURE);
}
else
{
fscanf(fp, "%d", &numElem); //scans for the first value which will tell the number of values to be stored in the array
int i = 0;
int num;
while (i <= numElem) //scans through and gets the all the values and stores them in the array.
{
fscanf(fp, "%d", &num);
arr[i] = num;
i++;
}
fclose(fp);
}
}
***note: My sort and swap method work perfectly so I have omitted them from the code as the error happens before they are even called.
you said,
The filename needs to be read in from the command line as well.
However, you are using:
char* filename = "numbers.txt";
and
fp = fopen(filename, "r"); // open file begin reading
No matter what you are passing in the command line, the file you are trying to open is "numbers.txt".
Things to try:
Use the full path name of "numbers.txt" instead of just the name of the file.
char* filename = "C:\\My\\Full\\Path\\numbers.txt";
If that doesn't work, you will probably have to deal with permissions issues.
Pass the file name from the command line, using the full path. That should work if there are no permissions issues.
if ( argc < 2 )
{
// Deal with unspecified file name.
}
char* filename = argv[1];
Pass the relative path of the file name. If you are testing your program from Visual Studio, you have to make sure that you use the path relative to the directory from where Visual Studio launches your program.
while (i <= numElem)
should be
while (i < numElem)
Because in fscanf(fp, "%d", &numElem); you are scanning the number of elements.
Notice that the array in C starts from 0, so if say numElem is 10 arr[10] does not exist which can be harmful because arr goes from arr[0] to arr[9]
Also, you should check if numElem is lower than 10 before the while(i < numElem) loop.

Reading a File into an array of Structures in C

still really new to C but starting to get the hang of it....
My program is supposed to create/write a file and store information from an array of structures. That part is fine. What im having trouble with is reading from that file back into an empty array of structures....
here's my structs:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX 100
struct Video {
char name[1024]; //name
int ranking; // Number of viewer hits
char url[1024]; // YouTube URL
};
struct Video Collection[MAX];
here's my load method which reads from my file back into my array of structures:
void load()
{
FILE *fileName;
fileName = fopen("ranking.dbm", "rb");
if (fileName != NULL){
fread (Collection,1,1,fileName);
}
else {
printf("ERROR");
}
}
also here is my write method:
void save()
{
FILE * pFile;
pFile = fopen ( "Ranking.dbm" , "wb" );
fwrite (Collection, 1 , sizeof(Collection), pFile );
fclose (pFile);
}
however when i print out my array collection after loading.... its empty... even though i can see my file in the project folder and open it and verify that the data is in there....
am i correct in thinking that i dont need a buffer since i don't need to do any processing on it before using it?
also since i've already statically allocated space for memory.... am i correct in thinking that i can just read directly into the array?
here is my print code:
void printall()
{
int i;
printf("\nCollections: \n");
for(i = 0; i < tail; i++)
{
printf("\nVideo Name: %s", Collection[i].name);
printf("\nRanking (Hits): %d", Collection[i].ranking);
printf("\nURL: %s", Collection[i].url);
printf("\n");
}
}
fread is in fact designed to read arrays of structures from a file, but you have to use it correctly.
fread's four parameters are as follows:
void * ptr, size_t size, size_t count, FILE * stream
The first parameter is where to put the data (in your case, Collection). The second parameter is the size of each array element: in your case, you want to put sizeof(struct Video). The third parameter is the number of elements you want to read, in your case, MAX. The fourth parameter is the file to read from.
If you want to read into an array like struct Video Collection[MAX], you would then use fread(Collection, sizeof(struct Video), MAX, file). fread will return the total number of elements read, which will be ≤ MAX.
I'm seeing a few issues here.. first how you read the file:
fread (Collection,1,1,fileName);
This will read into collection 1 byte from fileName into Collection
You should check the return status of fread(), when it's successful it tells you the total number of bytes to be read. (parameter 2 * parameter 3, or 1*1 in your case).
When I modify your read code like this:
fread(Collection, sizeof(struct Video), 1, fileName);
It does successfully read from the file... however you have a different problem now. Let's say your file contained this:
something 5 http://something.com
nothing 3 http://nothing.com
So (I think) that's the format for your file, a name (ASCII), a ranking (int), and URL (ASCII). Now let's say your main() function looked like this:
int main ()
{
load();
printall();
return 0;
}
What you'd get back on stdout would be something like:
Collections:
Video Name: something 6 http://something.com
nothing 3 http://nothing.com
Ranking (Hits): 0
URL:
The reason is because you declared your array with static (and very large) elements. The fread() will try to read in the sizeof(struct Video) which is 1024+4+1024 bytes, so unless every one of your lines is the exact size (1024 chars for name and url) then you're going to get what looks like messed up or empty data.
I would suggest reading until you hit a space instead and storing each value in the correct element instead of trying to read out the full file into an array.
EDIT:
If you want to populate your array like:
fread(myarray, sizeofstruct, numberofstructs, file);
You have to guarantee the data length. In your example you'd have to say "name is however many characters, + blank spaces = 1024" and same for URL. That seems to be a horrible space waster. The better bet is to populate your array one element at a time:
for(0 to last_element){
set myarray.name = data until first space
set myarray.ranking = (int)data until second space
set myarray.url = data until newline
}
You can use fscanf() to read until a whitespace.
Frankly if you're going to populate one element at a time I'd just use character pointers for name and url and dynamically assign memory so you don't have huge wasted arrays.
First I have to assume you meant struct Video Collection[MAX];else your upper part is invalid C.
Second: you are reading 1 byte into Collection.
Try
fread(Collection, sizeof(struct Video), MAX, fileName);
This will read up to MAX times chunks of sizeof(struct Video)bytes into Collection.

read data from file

How to read the data from a file to a structure?
I have a structure like
struct data
{
char name[20];
int age;
};
In file student_info.txt I have
ravi 12 raghu 14 datta 13 sujay 10 rajesh 13
and so on with many other names with ages. How can I read this from file to the structure data?
Reading this name and age should be a loop i.e for the first time I will read 'ravi' and '12', then I should pack this data in the structure and will pass the structure to a function as soon as the structure is set. It should come back to the file and read 'raghu' and '14' again pack the structure with this data, and this should be in a loop till I read all the data from the file
Can anyone please tell how to implement the logic?
The approach is:
Create an instance of an array of your struct, a file pointer for file access, and a counter variable
Open the file stream using the file pointer - check that it has been successfully opened. The file pointer will point to NULL if fopen() has failed
Read the data into the struct array using a loop. fscanf() returns the number of successful 'matches' with its format string - here it will be 2 (use this for the loop condition)
Close the file
An example of the code:
#include <stdio.h>
#define FILENAME "student_info.txt"
#define MAX_NO_RECORDS 50
struct data
{
char name[20];
int age;
};
int main(void)
{
/* Declare an array of structs to hold information */
struct data StudentInfo[MAX_NO_RECORDS];
/* Declare a file pointer to access file */
FILE *s_info;
int student_no = 0; /* holds no. of student records loaded */
/* open the file for reading */
s_info = fopen(FILENAME, "r");
/* Check if an error has occured - exit if so */
if(s_info == NULL)
{
printf("File %s could not be found or opened - Exiting...\n", FILENAME);
return -1;
}
printf("Loading data...\n");
while(fscanf(s_info, "%19s %i", StudentInfo[student_no].name, &StudentInfo[student_no].age) == 2)
{
/* refer to records with index no. (0 to (1 - no. of records))
individual members of structure can be accessed with . operator */
printf("%i\t%-19s %3i\n", student_no, StudentInfo[student_no].name, StudentInfo[student_no].age);
student_no++;
}
/* after the loop, student_no holds no of records */
printf("Total no. of records = %i\n", student_no);
/* Close the file stream after you've finished with it */
fclose(s_info);
return 0;
}
You just need to read data from this file and split that string based on some criteria. as your file is not properly formatted it would be difficult for you to parse data.
In your current scenario your file contain only first name and a digit you can easily parse this by detecting a Space Character in your string. but this could lead a problem if any of your name contains a space.
First of all separate each pair of word by some character such as : or ; or a tab or line break.
then between each separated string split it by space and then read all content of file in a char array then from that array try to find that special character which indicates one record.
Separate each record in a different char array then for each generated array again and then split it based on space char and load in your struct
This is just for explanation, original implementation may be different,
Student std = {first string, second integer};
Hope that document solves your problem http://www.softwareprojects.com/resources//t-1636goto.html

Resources