So I am trying to write a c file that has a student record database, once the file runs it is supposed to read student records from a binary file and add them to a linked list: the student struct looks like this :
typedef struct student {
char lname[10], initial, fname[10];
unsigned long SID;
float GPA;
struct student* next; /* pointer to the next student record */
} studentList;
I am using single linked lists for my data, and my code runs fine if I read and write the data with fscanf. However, once I started using fwrite and fread, everytime my program loads it would not load up the data from the text file correctly, when I check the binary file it seems it has data in it. Here is what I have for my load and write data functions:
void printRecords() {
FILE *fPointer = fopen("data.bin", "w");
studentList *newStudent = head;
while (newStudent != NULL) { /*Loop through linked list starting from head node*/
fwrite(&newStudent, sizeof(newStudent), 1, fPointer);
newStudent = newStudent->next;
}
}
void loadRecords() {
studentList *cStudent;
FILE *fPointer = fopen("data.bin", "r");
int counter = 0;
int x = 0;
int n = 0;
while (n != 0) {
printf("test\n");
if (fPointer == NULL) {
break;
}
cStudent = (studentList *)malloc(sizeof(studentList));
n = fread(&cStudent, sizeof(cStudent), 1, fPointer);
x = cStudent->GPA;
printf("%d\n", x);
if (feof(fPointer)) { break; }
if (counter == 0) {
head = cStudent;
temp = (studentList *)malloc(sizeof(studentList));
temp = cStudent;
counter++;
}
temp->next = (studentList *)malloc(sizeof(studentList));
temp->next = cStudent;
temp = temp->next;
}
fclose(fPointer);
}
so what am I doing wrong as right now it does not read anything into my list, it seems like it writes but not sure if it even writes the correct data, I have spent a long time trying to figure this out and have been stuck on it for a while now, thanks in advance.
To read write to binary files in c.
FILE *fPointer=fopen("data.bin","wb"); //to write file
FILE *fPointer=fopen("data.bin","rb"); //to read file
There are multiple problems with your code.
printRecords has problems:
you should use binary mode.
you write the contents of the pointer and some indeterminate contents instead of whet the pointer points to. This is actually undefined behavior.
you forgot to close the file.
the value written to the file for the next member is meaningless. Writing the same records may produce different file contents for different runs.
Here is a corrected version that returns the number of records written or -1 if the file could not be opened:
int printRecords(void) {
FILE *fPointer = fopen("data.bin", "wb");
studentList *sp;
int n = 0;
if (fPointer == NULL)
return -1;
/* Loop through linked list starting from head node */
for (sp = head; sp != NULL; sp = sp->next) {
n += fwrite(sp, sizeof(newStudent), 1, fPointer);
}
fclose(fPointer);
return n;
}
loadRecords has even more problems:
binary mode should be used too.
the way you test for end of file is incorrect.
the way you link records does not work either.
Here is a corrected version of loadRecords that returns the number of records read or -1 if the file could not be opened:
int loadRecords(void) {
studentList student, *newsp, *sp;
FILE *fPointer = fopen("data.bin", "rb");
int n = 0;
if (fPointer == NULL)
return -1;
while (fread(&student, sizeof(student), 1, fPointer) == 1) {
n++;
student.next = NULL; // value read from file is meaningless
newsp = malloc(sizeof(studentList));
*newsp = student;
if (head == NULL) {
head = newsp;
} else {
/* append the new record to the list */
for (sp = head; sp->next; sp = sp->next)
continue;
sp->next = newsp;
}
}
fclose(fPointer);
return n;
}
Be aware that storing binary data to the file system this way is not portable. The representation of integers and floats may differ from one platform to another, as well as alignment of the structure members, especially the next pointer which is useless in the file anyway. This file should only be read back on the same platform, with the same program that wrote it, making it a poor choice for backup or persistent storage.
There are problems in the file write function, mainly the data pointer and size:
void printRecords(){
FILE *fPointer = fopen("data.bin","wb"); // add "b" mode (MSVC)
if (fPointer == NULL) // check file opened
exit(1); // or report "Cannot open file"
studentList *newStudent = head;
while(newStudent !=NULL)
{
fwrite(newStudent, sizeof(*newStudent), 1, fPointer); // remove the & and add a *
newStudent = newStudent->next;
}
if (fclose(fPointer)) // close the file
exit(1); // or report "Failed to close file"
}
Related
So, i need a code to read a TXT file with 5 contents each line, and store each line values in a variable. but the number of lines in the TXT will vary. so i thought of excecuting something like this:
int counter=0;
char nome="TXT NAME.txt";
FILE *f = fopen(NAME,"r");
do{
if(!feof(p))
counter++;
}while(!feof(f));
fclose(f)
int X[counter][5];
so when declaring like this, X will have the number of lines of the file. but the issue is that to store the values, i would need to open and read the file again. is there a way to save the values while declaring the variable so i dont need to open twice ?
How to read a file in C and at the same time create a variable with the number of lines?
is there a way to save the values while declaring the variable so i dont need to open twice ?
A nice way is to read one line at a time with fgets(), copy the buffer with strdup() and save in a linked list of your creation.
Some pseudo-code to get you going.
f = open file
exit if unable to open
line_count = 0;
list = LL_Create()
char buffer[100];
while (fgets(buffer, sizeof buffer, f)) {
Maybe special handling here to deal with lines longer than 100
char *s = strdup(buffer);
LL_Append(list, s);
line_count++;
}
fclose(f);
// Use the link-list of strings
// When done
while !LL_Empty(list)
s = LL_Pop(list)
free(s);
LL_Destroy(list)
You can use realloc to gradually expand your "array".
But a simpler approach is to use a linked list, that way the list just grows as you read the file.
typedef struct
{
char data[5];
Line* next;
} Line;
Line* first = NULL;
Line* last = NULL;
char line[128];
while (fgets(line, sizeof(line), f)
{
Line* tmp = malloc(sizeof(Line));
memcpy(tmp->data,line,5);
tmp->next = NULL;
if (first == NULL)
{
first = last = tmp;
}
else
{
last->next = tmp;
last = tmp;
}
}
Now to go through the lines
for(Line* p = first; p != NULL; p = p->next)
{
p->data ...
}
I have this problem in my c program when I start to reinsert the contents of my file and save a new one. It fails in the while loop and i don't understand why it does that if i have some contents to reinsert from it.
here's my code:
'''
void init(){
char pn[30],pd[30],pp[30];
if ((flptr = fopen("MASTER.dat","r+")) == NULL) {
printf("Couldnt Get Cred");
return;
}
fscanf(flptr,"%s %s %s",pn,pd,pp);
while(!feof(flptr)){
r = (struct Records *) malloc(sizeof(struct Records));
int fr = fscanf(flptr,"%s %s %f",r->PartNum,r->PartDesc, &r->PartPrice);
if(fr == EOF){
printf("HERE");
break;
}
if(head == NULL){
head = r;
}
else{
tail->next = r;
}
tail = r;
}
fclose(flptr);
}
void put(){
if ((flptr = fopen("MASTER.dat","r")) == NULL) {
printf("Couldnt Get Cred");
return;
}
r = head;
fprintf(flptr,"PartNumber PartDescription PartPrice\n");
while (r != NULL){
fprintf(flptr,"%s %s %f\n", r->PartNum, r->PartDesc, r->PartPrice);
r = r->next;
}
fprintf(flptr,"Changes SAVED.");
fclose(flptr);
}
In the function put you open the file to read it, not to write, so your fprintf will have no effect and the file not be even created
if ((flptr = fopen("MASTER.dat","r")) == NULL) {
must be
if ((flptr = fopen("MASTER.dat","w")) == NULL) {
If later you try to read that non existing file with init you will not success
Out of that put and init use the global variable r and modify it, I encourage you to use a local variable to avoid possible problems
Why do you open file file with "r+" in init while you only read it ?
When you read string through (f)scanf I encourage you to limit the length to not write out of the receiver with an undefined behavior, and to always check the result, so for instance replace
fscanf(flptr,"%s %s %s",pn,pd,pp);
by
if (fscanf(flptr,"%29s %29s %29s",pn,pd,pp) != 3) {
printf("invalid file contain\n");
return;
}
I know how to hardcore a program to receive a file but when I try a similar tactic with scanf nothing happens. I mean that I have an error check that looks to see if it exist and if it has the right format but everytime I enter the filename it doens't print the printf statement below the scanf. Why is that? I also found out that I am opening the file but the while statement is infinite. Which doesn't make sense. I have tried another solution shown below but same results.
void parseFile(struct student_record_node** head)
{
FILE*input;
const int argCount = 4;
char filename[100]="";
const char rowformat[] = "%20s %20s %d %d";
struct student_record record;
struct student_record_node* node = NULL;
printf("\n Please Enter the FULL Path of the .txt file you like to load. \n");
scanf("%s",filename);
input = fopen(filename, "r");
printf("I am here");
if(input == NULL)
{
printf("Error: Unable to open file.\n");
exit(EXIT_FAILURE);
}
while(!feof(input))
{
/* creating blank node to fill repeatedly until end of document*/
memset(&record, 0, sizeof(struct student_record));
if(fscanf(input, rowformat, record.first_name_,record.last_name_,&record.student_id_,&record.student_age_) != argCount)
{
continue;
}
/* set node into the doubly linked list */
node = student_record_allocate();
/* copies values from the blank node reading from document into node in my linked list */
strcpy(node->record_->first_name_, record.first_name_);
strcpy(node->record_->last_name_, record.last_name_);
node->record_->student_id_ = record.student_id_;
node->record_->student_age_ = record.student_age_;
/* check if node right after absolute head is empty if so fills it */
if(*head == NULL)
{
*head = node;
}
else
{
printf(" stuck in loop\n");
/* if current isn't null start linking the node in a list */
appendNode(head,node);
}
}
fclose(input);
printf(" end of parsefile");
}
When I got to the parsefile() function and enter NEW.txt which is in the correct format and inside the same folder as the program itself. I know that my check is working when I enter a .txt file that doesn't exist or that is empty it gets caught like it should.
The expected behavior is that the program should load this list from new.txt and load it into a doubly linked list. Then return to a menu that gives user options. The doubly linked listed can then be manipulated such as add students manipulate data, deleting, saving and printing current roster. I have trouble using gdb with this program since I receive new.txt from parsefile.
Sample of New.txt contents. (Its just First Name, Last Name, Id, Age)
Belinda Homes 345 50
Scott Crown 456 18
Failed Solution: Using fgetc instead of feof
int c = fgetc(input);
while(c != EOF)
{
printf("\n in loop \n");
/* creating blank node to fill repeatedly until end of document*/
memset(&record, 0, sizeof(struct student_record));
if(fscanf(input, rowformat, record.first_name_,record.last_name_,&record.student_id_,&record.student_age_) != argCount)
{
continue;
}
/* set node into the doubly linked list */
node = student_record_allocate();
/* copies values from the blank node reading from document into node in my linked list */
strcpy(node->record_->first_name_, record.first_name_);
strcpy(node->record_->last_name_, record.last_name_);
node->record_->student_id_ = record.student_id_;
node->record_->student_age_ = record.student_age_;
/* check if node right after absolute head is empty if so fills it */
if(*head == NULL)
{
*head = node;
}
else
{
printf(" stuck in loop\n");
/* if current isn't null start linking the node in a list */
appendNode(head,node);
}
c = fgetc(input);
}
This is the easiest way to read from file and print out. I assume you want to print the file or do something with it.
int c;
FILE *file;
file = fopen("test.txt", "r");
if (file) {
while ((c = getc(file)) != EOF){
putchar(c);
}
fclose(file);
}
The title is my best guess at the problem at the moment, but I'm not completely sure.
I'm trying to read from a file that contains numbers and strings that start with a character(only strings start with a char). This file is created earlier in the program by code that should execute before this file is needed.
main
int main(void){
//name of the file to accessed
char *fileName = "input.txt";
node *head, *tail;
node *symListHead = NULL;
head = tail = createNode();
//creates the file with no comments exits if the file is not created
if(cleanFile(fileName) == 0){
exit(1);
}
//create the head of the linked list containing all tokens
head = createSymbolList();
//this should create the file and fill in called "lexemelist.txt"
printTableAndList( head );
//create a sym list of ints for comparison to enumerated types
symListHead = generateSymList();
//recursiveParse( symListHead );
return 0;
}//end main
So on the line with printTabelAndList(head) the file "lexemelist.txt" should be created and filled. And in symListHead = generateSymList() that file should be used. But if I run the code with symListHead = generateSymList() commented out the file is created and filled as it should be, but if the line is left in the file is left empty leading and nothing is read from the empty file.
lexemelist.txt contents with the line symListHead = generateSymList(); left in
29 2 x 17 2 y 18 21 2 x 20 2 y 4 3 56 18 22 19
lexemelist.txt contents with the line symListHead = generateSymList(); removed
genetateSymList()
symNode *generateSymList(){
FILE *fp;
char buff[255];
symNode *head, *temp;
head = temp = createSymNode();
fp = fopen("lexemelist.txt" , "r");
if(fp == NULL){
printf("\nfile not found.\n");
return NULL;
}
//this line is skicped since the file is empty for some reason
while(fscanf(fp, "%s", buff) != EOF)
{
if(isalpha(buff[0]))
continue;
printf("%d", atoi(buff));
temp->sym = atoi(buff);
temp->next = createSymNode();
temp = temp->next;
}//end while
fclose(fp);
return head;
}
printTableAndList()
void printTableAndList(node *head){
FILE *tfp, *lfp;
tfp = fopen("lexemetable.txt", "w");
lfp = fopen("lexemelist.txt", "w");
fprintf(tfp , "lexeme token type\n\n");
generateTableAndList(head, tfp, lfp);
}
Never mind I forgot to close my files in the printTableAndList() function.
I am currently writing a program to read a file and then save the information read to a new file. However when writing the second file no data is saved.
I am using a struct to help break up the text file that I want to use
struct file{
int source;
int destination;
int type;
int port;
char data[50];
};
After reading the file I have created a function to break up the data and print it to the screen
int parseFile(int countData, struct file *storedData)
{
FILE *in_File;
char buff[1000];
while(fgets(buff, 1000, in_File)!=NULL)
{
printf("%s", buff);
storedData[countData].source = atoi(strtok(buff, ":"));
storedData[countData].destination = atoi(strtok(0, ":"));
storedData[countData].type = atoi(strtok(0, ":"));
storedData[countData].port = atoi(strtok(0, ":"));
strcpy(storedData[countData].data, strtok(0, ":\n") );
}
}
and finally I created a function to save the file
void saveFile(int countData, struct file *storedData)
{
FILE *in_File;
char fileLocation[40];
int i = 0;
printf("\nEnter a File Name to save:");
scanf("%s", fileLocation);
if ((in_File = fopen(fileLocation, "w")) == NULL){
puts(" \n Could not point to the file.");
}else{
for(i=0;i<countData;i++)
{
fprintf(in_File, "%04d:%04d:%04d:%04d:%s \n",
storedData[i].source,
storedData[i].destination,
storedData[i].type,
storedData[i].port,
storedData[i].data );
}
}
fclose(in_File);
}
In the main function I used malloc to allocate the size of the struct
int main()
{
struct file *storedData;
storedData = malloc(sizeof(struct file));
int countData = 0;
banner();
readFile(countData, storedData);
parseFile(countData, storedData);
saveFile(countData, storedData);
return 0;
}
When getting the data and running it through my parseFile function each line is written out to me line by line
Example of data output:
0001:0002:0003:0021:CLS
0003:0004:0002:0180:100000000000000000030
0006:0003:0002:0041:100000000000000000019
0006:0002:0002:0060:100000000000000000020
However when saved to a file none of this output is stored and I was wondering what I could do to fix it
EDIT:
Here is the readFile function:
void readFile(int countData, struct file *storedData)
{
FILE *in_File;
char fileLocation[40];
printf("\nEnter file name: \n");
scanf("%s", fileLocation);
in_File = fopen(fileLocation, "r");
if(!in_File)
{
printf("\nError!\n");
}
}
storedData = malloc(sizeof(struct file));
Allocates enough for ONE file record.
Your routine parseFile() would write over that record repeatedly, but as it never increments countData it doesn't write any -- although even if it did, the parent routines would not receive your updated countData, as you would have had to pass an int * of &countData to let the parseFile() routine increment it.
Since for(i=0;i<countData;i++) is present, you never even read a single line, since 0 < 0 is false.
If you make that change, as soon as you hit the second record, you will overwrite memory and coredump because you only allocated one record worth of space in main()
Also in parseFile(), you have to open the file before reading it! Something like:
in_File = fopen(inputFileName, "r");
[edit] I see you updated with readFile() -- it needs to return the open FILE * for use by parseFile, and you will need to pass that FILE * to parseFile to use.