Reading random struct array element from binary file - c

I have an very big array (100k) in which elements are randomly placed with array index as primary key. I am trying to figure out a way to access array elements with fseek using index key as offset. For some reason no matter what offset i give, I get null back. Following is the simple representation of the problem.
Help appreciated. Sorry if this is a repost. I could not find similar question asked anywhere.
#include <stdlib.h>
#include <stdio.h>
typedef struct student
{
char *name;
int marks;
}student;
student class[] = {
[5]{"Jack",34},
[12]{"Jane",56},
[53]{"Joe",72}
};
main()
{
FILE *f, *f1;
student rec;
f = fopen("student.data", "wb");
fwrite(class, sizeof(student), sizeof(class), f);
fclose(f);
if((f1 = fopen("student.data","rb"))==NULL)
{
printf("\nError in Opening File\n");
exit(0);
}
/* I want to seek to 12th element in the array and print 'Jane' */
fseek(f1, sizeof(student) * 12, SEEK_SET);
fread(&rec, sizeof(student), 1, f1);
printf("Name:%s\n", rec.name);
fclose(f1);
}

Your example works for me. The only thing I had to change was to use double quotes instead of single quotes for the class array initialization:
cat student.c; gcc student.c; ./a.out
gives the following output:
#include <stdlib.h>
#include <stdio.h>
typedef struct student
{
char *name;
int marks;
}student;
student class[] = {
[5]{"Jack",34},
[12]{"Jane",56},
[53]{"Joe",72}
};
main()
{
FILE *f, *f1;
student rec;
f = fopen("student.data", "wb");
fwrite(class, sizeof(student), sizeof(class), f);
fclose(f);
if((f1 = fopen("student.data","rb"))==NULL)
{
printf("\nError in Opening File\n");
exit(0);
}
/* I want to seek to 12th element in the array and print 'Jane' */
fseek(f1, sizeof(student) * 12, SEEK_SET);
fread(&rec, sizeof(student), 1, f1);
printf("Name:%s\n", rec.name);
fclose(f1);
}
Name:Jane

Related

How can I write int value Binary and txt file in C?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
struct student
{
int stuNum;
int point;
};
int newStudent(FILE* dataf, FILE* indexf);
int main(void)
{
FILE* datafile;
FILE* indexfile;
indexfile = fopen("indexDosyasi.txt", "a+");
datafile = fopen("veriDosyasi.bin", "wb+");
newStudent(datafile, indexfile);
fclose(datafile);
fclose(indexfile);
return 0;
}
int newStudent(FILE* dataf, FILE* indexf)
{
indexf = fopen("indexfile.txt", "a+"); // txt file
dataf = fopen("datafile.bin", "wb"); // binary file
if (dataf == NULL)
{
printf("error");
return -1;
}
struct student* last = (struct student*)malloc(sizeof(struct student));
printf("number of student \n");
scanf("%d", last->stuNum);
fprintf(indexf, "%d\t", last->stuNum);
fwrite(last, sizeof(struct student), 1, dataf);
// fwrite(&last->stuNum,sizeof(struct student),1,dataf);
return 0;
}
Hi , i am trying student add system with c , my datafile have to be binary file and my indexfile have to be txt file , stuNum and point have to be int value but i cant add stunum and point into datafile and indexfile , i dont understand how can i add ? i read a lot answer about this question but i didnt find answer i want .When i run this code and enter the student number is 875,it says 66955608268424 in the indexfile(txt) and it says �*~ in the datafile(binary).i cant understand where is mistake?
Can you help me?
Try this way:
int newStudent(...)
{
write(fileName, "what you want to write", strlen("what you want to write"))
}
And if it doesn't works, try to tranform your int to a character string. I thing it is the itoa() or atoi() function.

Writing a struct into a file but reading it incorrectly

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student
{
int id;
int grade;
};
struct Student john;
int main()
{
john.id = 100;
john.grade = 80;
struct Student steve;
fwrite(&john, sizeof(struct Student), 1, stdout);
fread(&steve, sizeof(struct Student), 1, stdout);
printf("\n%d %d \n", steve.id, steve.grade);
return 0;
}
I am trying to write a struct into a file(in this case it is stdout), then I am trying to read it.
The values it prints are random, what could be the reason?
As already stated in the comments, you cannot read from stdout, however you could do it in a file that can be opened to read and write, you can even replace stdout with such file, for instance in UNIX uising unnamed pipes, chek C language. Read from stdout.
However you'd still need to reposition your file offset after you write so you can read from the beggining of the file, example:
int main()
{
john.id = 100;
john.grade = 80;
FILE* f = fopen("test", "w+"); // check return value..
struct Student steve;
fwrite(&john, sizeof(struct Student), 1, f); // same here...
fseek(f, 0, SEEK_SET); // <-- reset offset, (or lseek if you're using a file descritor)
fread(&steve, sizeof(struct Student), 1, f); // and here
printf("\n%d %d \n", steve.id, steve.grade);
fclose(f);
}

Reading bytes of a file into a structure

How would I go about reading a binary file and assigning their values to a structure? Each structure with its content will be written to a csv file.
I have this data file, it's a list of struct product entries.
Here is the Product structure in an h file:
struct product {
char code[15];
char name[50];
short int quantity;
double price;
}
typedef struct product *Product;
And here is how I'm trying to read each line into the Product structure and writing it to the csv file:
File *fp;
File *outFile;
fp = fopen("products.dat", "rb");
outFile = fopen("allproducts.csv", "w");
Product p;
while (fread(p, sizeof(Product), 1, fp) == 1) {
fwrite(p->code, sizeof(p->code), 1, outFile);
}
I don't think I'm reading the file correctly because when I try writing to the file, all I get are the question marks in a box in the csv file.
If the writer of the data file does not take care for the size of the struct it writes, the reader has trouble to make sense of it. I played around with it a bit and found out that a small adjustment is needed to get the proper result on my machine and OS.
#include <stdio.h>
#include <stdlib.h>
#define ADJUSTMENT 4
struct product {
char code[15];
char name[50];
short int quantity;
double price;
};
int main(void)
{
FILE *fp;
//FILE *outFile;
struct product p;
fp = fopen("products.dat", "rb");
if (fp == NULL) {
fprintf(stderr, "fopen failed\n");
exit(EXIT_FAILURE);
}
//outFile = fopen("allproducts.csv", "w");
while (fread(&p, sizeof(p) - ADJUSTMENT, 1, fp) == 1) {
printf("CODE: %s\n", p.code);
}
exit(EXIT_SUCCESS);
}
It is a well known problem with such simple attempts for writing binary data, it is just not portable and I'm pretty sure that the value of my adjustment does not fit for you. You need to find a method to make it portable.
I think your below code needs to be modified.
Your Original Code:
File *fp;
File *outFile;
fp = fopen("products.dat", "rb");
outFile = fopen("allproducts.csv", "w");
Product p;
while (fread(p, sizeof(Product), 1, fp) == 1) {
fwrite(p->code, sizeof(p->code), 1, outFile);
}
Modified Code:
File *fp;
File *outFile;
fp = fopen("products.dat", "rb");
outFile = fopen("allproducts.csv", "w");
Product p = malloc(sizeof(struct product));
while (fread(p, sizeof(struct product), 1, fp) == 1) {
fwrite(p->code, sizeof(p->code), 1, outFile);
}
Problems are:
p is a pointer to struct as it is of type Product which is a pointer to struct, you need to allocate memory for it before you can use it
In fread you are passing sizeof(Product) which essentially passes the size of the pointer, whereas you want to read the sizeof the structure product.
The other problems which I haven't corrected is checking the return values of fopen,malloc etc which I leave it to you. : )
Hiding pointers behind typedefs is error prone and leads to confusing code.
p is defined as a pointer to a struct product, but is not initialized: passing its value to fread as the destination invokes undefined behavior.
sizeof(Product) is the size of the pointer, not the size of the structure.
Do not define Product as a pointer, make it a typedef for the structure if you can, or do not use the typedef if you cannot:
struct product {
char code[15];
char name[50];
short int quantity;
double price;
};
typedef struct product Product;
Adjust the code and write the string to the csv file, not the complete array of chars:
File *fp;
File *outFile;
fp = fopen("products.dat", "rb");
outFile = fopen("allproducts.csv", "w");
struct product prod;
while (fread(&prod, sizeof(prod), 1, fp) == 1) {
fprintf(outFile, "%s\n", prod.code);
}
Note that this will be incorrect if the product code contains embedded spaces, ,, " or newline characters.

Why does the program only save some, but not all, of data to binary file?

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;
}

read data from file into array of structures

Hi I'm trying to read data from file into array of structures I tried to use fgets but got the error saying that RECORD type cannot be convert to char* this is what i have so far
#include <stdio.h>
#include <stdlib.h>
#define MAX_NAME 20
#define FILE_NAME 50
#define LIST_SIZE 50
//void getData(RECORD name[], RECORD score)
typedef struct RECORD
{
char *name;
float score;
}RECORD;
int main (void)
{
// Declarations
FILE *fp;
char fileName[FILE_NAME];
RECORD list[LIST_SIZE];
int count = 0;
// Statements
printf("Enter the file name: ");
gets(fileName);
fp = fopen(fileName, "r");
if(fp == NULL)
printf("Error cannot open the file!\n");
while (fgets(list[count], LIST_SIZE, fp) != NULL)
{
count++;
}
return 0;
}
the error occurs in the fgets statement inside while loop, how can i fix this and read data into array of structures?
thank in advance
fgets is used to input a string from a text file one line as a unit.
e.g.)
char input_line_buff[128];
fgets(input_line_buff, 128, fp);
read-in
input_line_buff:(contents eg) "name 66.6\n"
you do split , memory alloc and copy, and convert .
e.g.)
list[count].name = strdup("name");
list[count].score= atof("66.6");
count++;
Try this,
while(fgets((char *)list[count], SIZE/* data size to be read */, fp) != NULL)
{...}

Resources