I'm trying to make two functions, a writefile and a readfile. Obviously, writefile will write to a file using the contents from the struct pokemon, and readfile will read the file line by line and get the contents of the file. For example, after writing to a file successfully with two pokemon in the struct, the text file will look like this:
30
Pikachu
12
Charizard
The read file is supposed to store the level and name in the struct, and I'm currently having trouble. I've already been told that using fgets is correct, but I'm not doing it correctly.
struct pokemon
{
int level;
char name[30];
};
int readfile(struct pokemon pokearray[], int* num, char filename[])
{
FILE * fp;
int i = 0;
fp = fopen(filename, "r");
pokearray[10].level;
if (fp == NULL)
{
printf("errorrr");
return -1;
}
while (!feof(fp))
{
fgets(pokearray[i].level, 10, fp);
}
fclose(fp);
return 0;
}
I have an input file named cstats.txt that only has one line:
name:age:phone:city
And I have a struct Citizen:
typedef struct {
char name[100];
int age;
char city[100]
char phone[10];
} Citizen;
I need to read from the file and save the attributes in the struct:
I tried:
Citizen a;
fread(a, sizeof(Citizen), 1, "cstats.txt");
But I think it isn't saving the attributes.
I searched here on SO but all I found was to read line by line.
Thank you!
fread is not the best(simplest) tool for this job, use fgets instead.
Read the whole line, after that parse the fields with something like sscanf, example:
char line[250];
Citizen a;
FILE *file = fopen("cstats.txt", "r");
if (file != NULL)
{
if (fgets(line, sizeof line, file))
{
sscanf(line, "%99[^:]:%d:%9[^:]:%99[^\n]", a.name, &a.age, a.phone, a.city);
}
}
About fread, a good use case for it is to read data from a file that was writen to by fwrite, that would guarantee that the data would be appropriately formated to be read directly to a buffer like the one used to write, for instance:
Citizen a;
Citizen b;
FILE *file = fopen("file.txt", "w+"); // open to read and write
fwrite(&a, sizeof a, 1, file); // write Citizen a to file
fseek(file, 0, SEEK_SET); // reset file position indicator
// rewind(file); // alternative
fread(&b, sizeof b, 1, file); // read data directly to Citizen b
In the above code I skipped return value checking for simplicity purposes, you should do it in your code.
Live demo
I've made a program that reads a file named "books.txt" and displays its contents. In the file, there is a price for each of the 4 books.
I'm not sure how to make my program increase the price of the book (percent) depending on user inputs; for example, the user is prompted to enter a number and that number (which is a percentage) is multiplied by the price of each book and gives the updated price.
How do I do it?
Here's my code:
int main() {
/* File pointer to hold reference to our file */
FILE * fPtr;
char buffer[BUFFER_SIZE];
int totalRead = 0;
/*
*/
fPtr = fopen("books.txt", "r");
if (fPtr == NULL) {
printf("Unable to open file.\n");
printf("Please check whether file exists and you have read privilege.\n");
exit(EXIT_FAILURE);
}
printf("File opened successfully. Reading file contents line by line. \n\n");
while (fgets(buffer, BUFFER_SIZE, fPtr) != NULL) {
totalRead = strlen(buffer);
/*
*/
buffer[totalRead - 1] = buffer[totalRead - 1] == '\n'
? '\0'
: buffer[totalRead - 1];
printf("%s\n", buffer);
}
fclose(fPtr);
return 0;
}
You can read the file and store all the book data in some array of struct such as
typedef struct {
int id;
char *title;
char *author;
int year;
float price;
}Book;
Then ask for the percentage to the user and re-write the file calculating the new price for each Book (price += percentage/100)
To re-write the file you need to open the file with "w permits. You can check for documentation here. Don't forget to close fPtr before opening the same file again with write permits
Is there a way to determine how many elements an array of struct should have or allocate dynamic memory for a struct array in C? The code opens a binary file and reads it into a struct array. I can place an arbitrary value like 3000, however when I go to print it gives me the garbage values after the file ends. I'm looking for a way to set my array to the size of the file that is being read or limit it to the useful data. I imagine a function like malloc() just haven't been able to find examples with struct arrays that aren't static.
#include "function.h"
int main() {
FILE *fp; // file pointer
STRUCTNAME structVar[]; //typdef array of structs
fp = fopen("file.bin", "rb"); //binary file to read into array
if(fp == NULL) { // error check file opening
printf("error\n");
} else {
//while !feof to read file into array
while(!feof(fp)) {
fread(structVar, sizeof(structVar), 1, fp);
printStructData(structVar);
}
fclose(fp); // closes file
}
return 0;
}
STRUCTNAME *structVar;
FILE* fp = fopen(...);
if(fp)
{
long fileSize;
fseek(fp, 0 , SEEK_END);
fileSize = ftell(fp);
fseek(fp, 0 , SEEK_SET);
structVar = malloc(sizeof(STRUCTNAME) * (fileSize / sizeof(STRUCTNAME) + 1)); //just in case if the file length is not exactly sizeof(STRUCTNAME) * n
...
fclose(fp);
}
After write data to the binary file, I changed the mode to "r" to read the file. The name of who is correct, but color and education are empty. Age is returned as a large integer number, which is I guess the address of the variable. So, what is wrong here?
Update: The answer of Retired Ninja and Thornkey almost solve my problem. The rest is if input of age is 26, but not other numbers, the program will not write correct input to file. Anyone know what is wrong here?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXLEN 100
typedef struct Person{
char name[MAXLEN];
int age;
char color[MAXLEN];
char education[MAXLEN];
} Person;
void create_person(Person *who){
printf("name: ");
fscanf(stdin, "%s", who->name);
printf("age: ");
fscanf(stdin, "%d", &(who->age));
printf("color: ");
fscanf(stdin, "%s", who->color);
printf("education: ");
fscanf(stdin, "%s", who->education);
}
void print_record(Person *who){
printf("name: %s\n", who->name);
printf("age: %d\n", who->age);
printf("color: %s\n", who->color);
printf("education: %s\n", who->education);
}
void load_db(FILE *fp, Person *who){
int result = fread(who, sizeof(who), 1, fp);
if(!result)
//printf("result%d", result);
printf("cannot load database");
}
FILE *connect_db (char *file_name, char *mode, Person *who){
FILE *fp = (FILE *)malloc(sizeof(100));
// open stream and load database from the file
if(strcmp(mode, "w") == 0){
fp = fopen(file_name, mode);
//load_db(conn); // load data from file
}else if(strcmp(mode, "r") == 0){
fp = fopen (file_name, mode);
load_db(fp, who); // load data from file
}else{
printf("incorrect mode");
}
return fp;
}
// save database to file
int save_db (FILE *fp, Person *who){
int result = fwrite(who, sizeof(who), 1, fp);
if(result){
return 0; // successfully save db
}
printf("cannot save db");
}
int main(int argc, char *argv[])
{
char answer[MAXLEN];
Person person;
Person *who = &person;
FILE *fp;
create_person(who);
fp = connect_db("record2.dat", "w", who);
save_db(fp, who);
print_record(who);
free(fp);
fclose(fp);
return 0;
}
fread() reads individual bytes. You want to read in numbers which you have printf'd.
Your files will look like this:
name: Samuel Thornkey
age: 24
colour: blue
education: PHD in computer science
But when you use fread, the program directly reads bytes from the file and fills them into the record. Your person will then contain:
char name[MAXLEN]: first MAXLEN characters i.e. "name: Samuel Thornkey\n age: 24\ncolour: blue\n" or something similar
int age: the rest of the characters, encoded as bytes, hence very large number
char color[MAXLEN]: empty
char education[MAXLEN]: empty
Instead, use fscanf:
fscanf(fp,"name:%s ",&who->name);
fscanf(fp,"age:%d ",&who->age);
and so on.
It's likely to be this:
free(fp);
fclose(fp);
You are not permitted to free memory that wasn't given to you by malloc (or realloc).
And, yes, you may think you've allocated it inside connect_db but (1) that's totally unnecessary, and (2) you overwrite the pointer when you call fopen.
In addition, save_db is using the size of the who pointer which will most likely not be the same as the type it points to.
So, make the following changes:
get rid of the call to malloc, just use FILE *fp; within connect_db.
get rid of the free(fp) within main.
in save_db, use sizeof(Person) rather than sizeof(who).
Here's a fixed version that may be of some help to you. It successfully fills in a Person, prints it, and writes it to the file. Then it reads the data back from the file into a different Person and prints it.
The structure is similar to what you had, but I made all the reading/writing explicit so you'd see the steps. In general it isn't a good idea to have functions that perform extra duties you might not always want. I also made create_person require no input for faster testing. Your input code looked okay, I just didn't want to type it every time.
One thing to keep in mind, if you plan to write binary data to a file you should open the file in binary mode ("wb" or "rb") to avoid line ending translation on systems that perform that on text files.
You might also consider that a file written on one system may not be readable on a different system if the size of the Person structure changes due to different alignment or int being a different size. Probably not an issue for you, but if it becomes one you might look into a different serialization scheme.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXLEN 100
typedef struct Person
{
char name[MAXLEN];
int age;
char color[MAXLEN];
char education[MAXLEN];
} Person;
void create_person(Person *who)
{
strcpy(who->name, "Fred Smith");
who->age = 21;
strcpy(who->color, "Red");
strcpy(who->education, "Some School");
}
void print_record(Person *who)
{
printf("name: %s\n", who->name);
printf("age: %d\n", who->age);
printf("color: %s\n", who->color);
printf("education: %s\n", who->education);
}
void load_db(FILE *fp, Person *who)
{
int result = fread(who, sizeof(*who), 1, fp);
if(!result)
printf("cannot load database");
}
FILE *connect_db(char *file_name, char *mode, Person *who)
{
FILE *fp = NULL;
if(strcmp(mode, "w") == 0)
{
fp = fopen(file_name, mode);
}
else if(strcmp(mode, "r") == 0)
{
fp = fopen(file_name, mode);
}
else
{
printf("incorrect mode");
}
return fp;
}
int save_db(FILE *fp, Person *who)
{
int result = fwrite(who, sizeof(*who), 1, fp);
if(result)
{
return 0;
}
printf("cannot save db");
return -1;
}
int main(int argc, char *argv[])
{
FILE* fp = NULL;
Person who1;
Person who2;
create_person(&who1);
print_record(&who1);
fp = connect_db("record2.dat", "w", &who1);
save_db(fp, &who1);
fclose(fp);
fp = connect_db("record2.dat", "r", &who2);
load_db(fp, &who2);
print_record(&who1);
fclose(fp);
return 0;
}