Files as databases: writing and reading files
I am Jyotiprakash, a deeply driven computer systems engineer, software developer, teacher, and philosopher. With a decade of professional experience, I have contributed to various cutting-edge software products in network security, mobile apps, and healthcare software at renowned companies like Oracle, Yahoo, and Epic. My academic journey has taken me to prestigious institutions such as the University of Wisconsin-Madison and BITS Pilani in India, where I consistently ranked among the top of my class.
At my core, I am a computer enthusiast with a profound interest in understanding the intricacies of computer programming. My skills are not limited to application programming in Java; I have also delved deeply into computer hardware, learning about various architectures, low-level assembly programming, Linux kernel implementation, and writing device drivers. The contributions of Linus Torvalds, Ken Thompson, and Dennis Ritchie—who revolutionized the computer industry—inspire me. I believe that real contributions to computer science are made by mastering all levels of abstraction and understanding systems inside out.
In addition to my professional pursuits, I am passionate about teaching and sharing knowledge. I have spent two years as a teaching assistant at UW Madison, where I taught complex concepts in operating systems, computer graphics, and data structures to both graduate and undergraduate students. Currently, I am an assistant professor at KIIT, Bhubaneswar, where I continue to teach computer science to undergraduate and graduate students. I am also working on writing a few free books on systems programming, as I believe in freely sharing knowledge to empower others.
Below is a simple C program that uses an array of structs to input details about students and then writes the structures to a file as a database. The program assumes that each student has a name, roll number, and marks.
Certainly! If you want to write each student structure individually in a loop, you can modify the writeToFile function as follows:
#include <stdio.h>
// Define the structure for a student
struct Student {
char name[50];
int rollNumber;
float marks;
};
// Function to input details of students
void inputStudentDetails(struct Student *students, int numStudents) {
for (int i = 0; i < numStudents; ++i) {
printf("Enter details for student %d:\n", i + 1);
printf("Name: ");
scanf("%s", students[i].name); // Assuming names don't have spaces
printf("Roll Number: ");
scanf("%d", &students[i].rollNumber);
printf("Marks: ");
scanf("%f", &students[i].marks);
}
}
// Function to write student structures to a binary file individually
void writeToFile(struct Student *students, int numStudents, const char *filename) {
FILE *file = fopen(filename, "wb");
if (file == NULL) {
perror("Error opening file");
return;
}
for (int i = 0; i < numStudents; ++i) {
fwrite(&students[i], sizeof(struct Student), 1, file);
}
fclose(file);
}
int main() {
int numStudents;
printf("Enter the number of students: ");
scanf("%d", &numStudents);
// Allocate memory for an array of structs
struct Student *students = (struct Student *)malloc(numStudents * sizeof(struct Student));
if (students == NULL) {
perror("Error allocating memory");
return 1;
}
// Input details of students
inputStudentDetails(students, numStudents);
// Write student structures to a binary file individually
writeToFile(students, numStudents, "student_database.bin");
// Free allocated memory
free(students);
printf("Student details written to binary file successfully.\n");
return 0;
}
Let's go through the code step by step to understand its functionality:
#include <stdio.h>
This line includes the standard input/output library, which provides functions like printf and scanf.
// Define the structure for a student
struct Student {
char name[50];
int rollNumber;
float marks;
};
This defines a structure named Student with three members: name (a character array to store the student's name), rollNumber (an integer to store the roll number), and marks (a float to store the student's marks).
// Function to input details of students
void inputStudentDetails(struct Student *students, int numStudents) {
for (int i = 0; i < numStudents; ++i) {
printf("Enter details for student %d:\n", i + 1);
printf("Name: ");
scanf("%s", students[i].name);
printf("Roll Number: ");
scanf("%d", &students[i].rollNumber);
printf("Marks: ");
scanf("%f", &students[i].marks);
}
}
This function, inputStudentDetails, takes an array of Student structures (students) and the number of students (numStudents). It then uses a loop to input details for each student by prompting the user for the name, roll number, and marks.
// Function to write student structures to a binary file individually
void writeToFile(struct Student *students, int numStudents, const char *filename) {
FILE *file = fopen(filename, "wb");
if (file == NULL) {
perror("Error opening file");
return;
}
for (int i = 0; i < numStudents; ++i) {
fwrite(&students[i], sizeof(struct Student), 1, file);
}
fclose(file);
}
This function, writeToFile, takes an array of Student structures (students), the number of students (numStudents), and a filename as parameters. It opens a file with the specified filename in binary write mode ("wb"). If the file opening fails, it prints an error message using perror and returns from the function.
Inside the function, there's a loop that iterates over each student in the array and uses fwrite to write the student structure to the file. The third argument of fwrite is set to 1 because we are writing one structure at a time. The size of each structure is calculated using sizeof(struct Student).
int main() {
int numStudents;
printf("Enter the number of students: ");
scanf("%d", &numStudents);
// Allocate memory for an array of structs
struct Student *students = (struct Student *)malloc(numStudents * sizeof(struct Student));
if (students == NULL) {
perror("Error allocating memory");
return 1;
}
// Input details of students
inputStudentDetails(students, numStudents);
// Write student structures to a binary file individually
writeToFile(students, numStudents, "student_database.bin");
// Free allocated memory
free(students);
printf("Student details written to binary file successfully.\n");
return 0;
}
In the main function, the program begins by prompting the user to enter the number of students. It then dynamically allocates memory for an array of Student structures using malloc. If the memory allocation fails, it prints an error message and exits the program with a return value of 1.
The inputStudentDetails function is then called to gather details for each student from the user.
Finally, the writeToFile function is called to write each student structure to a binary file named "student_database.bin." After writing the data, the dynamically allocated memory is freed using free.
The program concludes by printing a success message and returning 0 from the main function, indicating successful execution.
Here's a program that opens the binary file created by the previous program, reads all structures into an array, takes a roll number from the user, finds the student from the array, and outputs details of that student to the console. It continues to take roll numbers until the user types "done":
#include <stdio.h>
// Define the structure for a student
struct Student {
char name[50];
int rollNumber;
float marks;
};
// Function to read student structures from a binary file individually
void readFromFile(struct Student *students, int numStudents, const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
perror("Error opening file");
return;
}
for (int i = 0; i < numStudents; ++i) {
fread(&students[i], sizeof(struct Student), 1, file);
}
fclose(file);
}
// Function to find and display details of a student by roll number
void findAndDisplayStudent(struct Student *students, int numStudents, int rollNumber) {
for (int i = 0; i < numStudents; ++i) {
if (students[i].rollNumber == rollNumber) {
printf("Student found!\n");
printf("Name: %s\n", students[i].name);
printf("Roll Number: %d\n", students[i].rollNumber);
printf("Marks: %.2f\n", students[i].marks);
return; // Student found, exit the function
}
}
// If loop completes, the student was not found
printf("Student with Roll Number %d not found.\n", rollNumber);
}
int main() {
int numStudents;
printf("Enter the number of students: ");
scanf("%d", &numStudents);
// Allocate memory for an array of structs
struct Student *students = (struct Student *)malloc(numStudents * sizeof(struct Student));
if (students == NULL) {
perror("Error allocating memory");
return 1;
}
// Read student structures from the binary file individually
readFromFile(students, numStudents, "student_database.bin");
// Take roll numbers until the user types "done"
while (1) {
int rollNumber;
printf("Enter a roll number (type 'done' to exit): ");
if (scanf("%d", &rollNumber) != 1) {
// Handle invalid input
printf("Invalid input. Exiting.\n");
break;
}
if (rollNumber == -1) {
// Exit the loop if the user types -1
break;
}
// Find and display details of the student
findAndDisplayStudent(students, numStudents, rollNumber);
}
// Free allocated memory
free(students);
return 0;
}
Let's break down the program step by step:
#include <stdio.h>
This line includes the standard input/output library, which provides functions like printf and scanf.
// Define the structure for a student
struct Student {
char name[50];
int rollNumber;
float marks;
};
This defines a structure named Student with three members: name (a character array to store the student's name), rollNumber (an integer to store the roll number), and marks (a float to store the student's marks).
// Function to read student structures from a binary file individually
void readFromFile(struct Student *students, int numStudents, const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
perror("Error opening file");
return;
}
for (int i = 0; i < numStudents; ++i) {
fread(&students[i], sizeof(struct Student), 1, file);
}
fclose(file);
}
This function, readFromFile, takes an array of Student structures (students), the number of students (numStudents), and a filename as parameters. It opens a binary file with the specified filename in read mode ("rb"). If the file opening fails, it prints an error message using perror and returns from the function.
Inside the function, there's a loop that iterates over each student in the array. It uses fread to read the student structure from the file into the array. The third argument of fread is set to 1 because we are reading one structure at a time. The size of each structure is calculated using sizeof(struct Student).
// Function to find and display details of a student by roll number
void findAndDisplayStudent(struct Student *students, int numStudents, int rollNumber) {
for (int i = 0; i < numStudents; ++i) {
if (students[i].rollNumber == rollNumber) {
printf("Student found!\n");
printf("Name: %s\n", students[i].name);
printf("Roll Number: %d\n", students[i].rollNumber);
printf("Marks: %.2f\n", students[i].marks);
return; // Student found, exit the function
}
}
// If loop completes, the student was not found
printf("Student with Roll Number %d not found.\n", rollNumber);
}
This function, findAndDisplayStudent, takes an array of Student structures (students), the number of students (numStudents), and a roll number as parameters. It searches for a student with the given roll number in the array and displays their details if found.
int main() {
int numStudents;
printf("Enter the number of students: ");
scanf("%d", &numStudents);
// Allocate memory for an array of structs
struct Student *students = (struct Student *)malloc(numStudents * sizeof(struct Student));
if (students == NULL) {
perror("Error allocating memory");
return 1;
}
// Read student structures from the binary file individually
readFromFile(students, numStudents, "student_database.bin");
// Take roll numbers until the user types "done"
while (1) {
int rollNumber;
printf("Enter a roll number (type 'done' to exit): ");
if (scanf("%d", &rollNumber) != 1) {
// Handle invalid input
printf("Invalid input. Exiting.\n");
break;
}
if (rollNumber == -1) {
// Exit the loop if the user types -1
break;
}
// Find and display details of the student
findAndDisplayStudent(students, numStudents, rollNumber);
}
// Free allocated memory
free(students);
return 0;
}
In the main function, the program starts by prompting the user to enter the number of students. It then dynamically allocates memory for an array of Student structures using malloc. If the memory allocation fails, it prints an error message and exits the program with a return value of 1.
The readFromFile function is then called to read each student structure individually from the binary file into the array.
The program enters a loop where it continuously takes roll numbers from the user until the user types "done" or enters -1. It uses scanf to read integer input, and if the input is not an integer, it prints an error message and exits the loop. If the user types -1, the loop also exits.
For each valid roll number, the findAndDisplayStudent function is called to search for and display details of the corresponding student.
The loop continues until the user types "done" or -1.
Finally, the dynamically allocated memory is freed using free. The program concludes by returning 0 from the main function, indicating successful execution.