⭐Introduction to File I/O in C: Understanding “Why Save Data” Before Using CSV to Write and Read Data
Author: IoT Smart Academy
1. Why Must We Learn “File I/O”?
- Memory is temporary: When the program stops or power is lost, the data is gone.
- Files are permanent: Stored on the hard drive, they remain after a reboot; they can also be used by Excel / Python / databases.
- Essential for courses/competitions/projects: Scores, equipment lists, and sensor logs all needtracing and reuse. Conclusion:No saving = demo program; saving = usable system.
2. “File Format” is a Set of Conventions
Defineshow to write and read a record. Focus on 3 points:
- Fields and order (Schema) 2) Delimiters (comma/tab) 3) Encoding and header (UTF-8, whether to write column names)
Recommended for beginners: CSV (UTF-8)
- One record per line,comma-separated,optional header, opens directly in Excel, and is the easiest across tools.
- Example (with header):
id,name,score
1001,Zhang,92.5
1002,Li,85.0
1003,Wang,77.5
3. Minimum Required API (Four Steps)
- Open:
<span>FILE *fp = fopen(path, "w/r/a"); if(!fp){ perror("open"); }</span> - Write:
<span>fprintf(fp, "%d,%s,%.2f\n", id, name, score);</span> - Read (more stable):
<span>fgets(line,sz,fp)</span>→<span>sscanf(line, "%d,%19[^,],%f", …)</span> - Close:
<span>fclose(fp);</span>Mnemonic:Open → Write/Read → Close; each step mustcheck the result.
4. A Working Example (Minimum Usable)
Writing to CSV (with header)
#include <stdio.h>
typedef struct {int id; char name[20]; float score; } Student;
int saveCSV(const char *path, const Student *a, int n) {
FILE *fp = fopen(path, "w"); // Overwrite
if (!fp) { perror("open"); return 0; }
fprintf(fp, "id,name,score\n"); // Header (recommended)
for (int i = 0; i < n; i++)
fprintf(fp, "%d,%s,%.2f\n", a[i].id, a[i].name, a[i].score);
fclose(fp);
return 1;
}
Reading from CSV (skipping header, parsing line by line)
#include <stdio.h>
typedef struct {int id; char name[20]; float score; } Student;
int loadCSV(const char *path, Student *a, int maxn, int *out_n) {
FILE *fp = fopen(path, "r");
if (!fp) { perror("open"); return 0; }
char line[256];
int n = 0;
// If there is a header, read one line and discard; if no header, delete this line
if (!fgets(line, sizeof(line), fp)) { fclose(fp); return 0; }
while (n < maxn && fgets(line, sizeof(line), fp)) {
if (sscanf(line, "%d,%19[^,],%f", &a[n].id, a[n].name, &a[n].score) == 3)
n++;
// else can print "bad line" warning
}
fclose(fp);
*out_n = n;
return 1;
}
5. Persisting CSV DataIntegrated into Two Practical Systems
✅ Student Score Management System · Pointer Upgrade Version (New 13 Save CSV / 14 Read CSV) C Language Pointer Practical: Student Score Management System · Pointer Upgrade Version (Menu + Struct Array + Sorting/Querying/Statistics)
#include <stdio.h>
#include <string.h>
#define MAX_STU 300
/* —— Data Structure —— */
typedef struct {
int id;
char name[20];
float score;
} Student;
typedef struct {
Student arr[MAX_STU];
int count;
} StudentDB;
/* —— Function Declarations —— */
void initDB(StudentDB *db);
int menu();
void addStudents(StudentDB *db);
void showAll(const StudentDB *db);
Student* findById(StudentDB *db, int id);
int findByNameFirst(const StudentDB *db, const char *name);
void updateById(StudentDB *db);
void deleteById(StudentDB *db);
void sortByScoreDesc(StudentDB *db);
void sortByScoreAsc(StudentDB *db);
void sortByIdAsc(StudentDB *db);
void statistics(const StudentDB *db);
void filterByRange(const StudentDB *db);
void batchBonus(StudentDB *db);
/* —— CSV Persistence —— */
int saveStudentsCSV(const char *path, const StudentDB *db);
int loadStudentsCSV(const char *path, StudentDB *db);
int readInt(const char *tip);
float readFloat(const char *tip);
/* —— Main Program —— */
int main() {
StudentDB db;
initDB(&db);
while (1) {
switch (menu()) {
case 1: addStudents(&db); break;
case 2: showAll(&db); break;
case 3: {
int id = readInt("Please enter student ID:");
Student *p = findById(&db, id);
if (p) printf("Found: %d %s %.2f\n", p->id, p->name, p->score);
else printf("Student ID not found.\n");
} break;
case 4: updateById(&db); break;
case 5: deleteById(&db); break;
case 6: sortByScoreDesc(&db); break;
case 7: sortByScoreAsc(&db); break;
case 8: sortByIdAsc(&db); break;
case 9: statistics(&db); break;
case 10: filterByRange(&db); break;
case 11: batchBonus(&db); break;
case 12: /* Save CSV */
if (saveStudentsCSV("students.csv", &db))
printf("✅ Saved to students.csv\n");
else
printf("❌ Save failed\n");
break;
case 13: /* Read CSV */
if (loadStudentsCSV("students.csv", &db)) {
printf("Read from students.csv, current count: %d\n", db.count);
showAll(&db);
} else {
printf("Read failed\n");
}
break;
case 0:
printf("System exited, goodbye!\n");
return 0;
default:
printf("Invalid option, please try again.\n");
}
}
}
/* —— Common Input —— */
int readInt(const char *tip) { int x; printf("%s", tip); scanf("%d", &x); return x; }
float readFloat(const char *tip){ float x; printf("%s", tip); scanf("%f", &x); return x; }
/* —— Initialization/Menu —— */
void initDB(StudentDB *db) { db->count = 0; }
int menu() {
int c;
printf("\n===== Student Score Management System · Pointer Upgrade Version (with CSV) =====\n");
printf("1 Input 2 Display 3 Search by ID 4 Search by Name 5 Update Score\n");
printf("6 Delete 7 Score Descending 8 Score Ascending 9 ID Ascending\n");
printf("10 Statistics 11 Range Filter 12 Batch Bonus\n");
printf("13 Save CSV 14 Read CSV\n");
printf("0 Exit\n");
printf("==============================================\n");
printf("Please enter option:");
scanf("%d", &c);
return c;
}
/* —— Basic Functions —— */
void addStudents(StudentDB *db) {
int n = readInt("Please enter the number of students to input:");
if (n <= 0) { puts("Number must be greater than 0."); return; }
if (db->count + n > MAX_STU) { printf("Insufficient capacity, can input %d more students.\n", MAX_STU - db->count); return; }
for (int i = 0; i < n; i++) {
Student s;
printf("Please enter ID Name Score (example: 1001 Zhang 85.5):\n");
scanf("%d %19s %f", &s.id, s.name, &s.score);
db->arr[db->count++] = s;
}
printf("Input completed, current total: %d\n", db->count);
}
void showAll(const StudentDB *db) {
if (db->count == 0) { puts("No data available."); return; }
printf("\nIndex\tID\tName\tScore\n");
for (int i = 0; i < db->count; i++)
printf("%d\t%d\t%s\t%.2f\n", i + 1, db->arr[i].id, db->arr[i].name, db->arr[i].score);
}
Student* findById(StudentDB *db, int id) {
for (int i = 0; i < db->count; i++) if (db->arr[i].id == id) return &db->arr[i];
return NULL;
}
int findByNameFirst(const StudentDB *db, const char *name) {
for (int i = 0; i < db->count; i++) if (strcmp(db->arr[i].name, name) == 0) return i;
return -1;
}
void updateById(StudentDB *db) {
int id = readInt("Please enter the ID to modify:");
Student *p = findById(db, id);
if (!p) { puts("ID not found."); return; }
float sc = readFloat("Please enter new score:");
p->score = sc;
printf("Updated: %d %s %.2f\n", p->id, p->name, p->score);
}
void deleteById(StudentDB *db) {
if (db->count == 0) { puts("No data available."); return; }
int id = readInt("Please enter the ID to delete:");
int pos = -1;
for (int i = 0; i < db->count; i++) if (db->arr[i].id == id) { pos = i; break; }
if (pos == -1) { puts("ID not found."); return; }
for (int i = pos; i < db->count - 1; i++) db->arr[i] = db->arr[i + 1];
db->count--;
printf("Deleted, current count: %d\n", db->count);
}
void sortByScoreDesc(StudentDB *db) {
for (int i = 0; i < db->count - 1; i++)
for (int j = 0; j < db->count - 1 - i; j++)
if (db->arr[j].score < db->arr[j + 1].score) {
Student t = db->arr[j]; db->arr[j] = db->arr[j + 1]; db->arr[j + 1] = t;
}
puts("Sorted by score in descending order."); showAll(db);
}
void sortByScoreAsc(StudentDB *db) {
for (int i = 0; i < db->count - 1; i++)
for (int j = 0; j < db->count - 1 - i; j++)
if (db->arr[j].score > db->arr[j + 1].score) {
Student t = db->arr[j]; db->arr[j] = db->arr[j + 1]; db->arr[j + 1] = t;
}
puts("Sorted by score in ascending order."); showAll(db);
}
void sortByIdAsc(StudentDB *db) {
for (int i = 0; i < db->count - 1; i++)
for (int j = 0; j < db->count - 1 - i; j++)
if (db->arr[j].id > db->arr[j + 1].id) {
Student t = db->arr[j]; db->arr[j] = db->arr[j + 1]; db->arr[j + 1] = t;
}
puts("Sorted by ID in ascending order."); showAll(db);
}
void statistics(const StudentDB *db) {
if (db->count == 0) { puts("No data available."); return; }
float max = db->arr[0].score, min = db->arr[0].score, sum = 0.0f; int pass = 0;
for (int i = 0; i < db->count; i++) {
float x = db->arr[i].score;
if (x > max) max = x; if (x < min) min = x; sum += x; if (x >= 60) pass++;
}
printf("Count: %d Highest: %.2f Lowest: %.2f Average: %.2f Pass Rate: %.2f%%\n",
db->count, max, min, sum / db->count, db->count ? 100.0f * pass / db->count : 0.0f);
}
void filterByRange(const StudentDB *db) {
if (db->count == 0) { puts("No data available."); return; }
float L = readFloat("Lower limit:"), R = readFloat("Upper limit:"); if (L > R) { float t=L; L=R; R=t; }
int found = 0; printf("\n=== [%.2f, %.2f] Range ===\n", L, R);
for (int i = 0; i < db->count; i++) {
float x = db->arr[i].score;
if (x >= L && x <= R) { printf("%d %s %.2f\n", db->arr[i].id, db->arr[i].name, x); found = 1; }
}
if (!found) puts("No matches.");
}
void batchBonus(StudentDB *db) {
if (db->count == 0) { puts("No data available."); return; }
float off = readFloat("Please enter bonus offset (e.g. +2 / -1.5):");
for (int i = 0; i < db->count; i++) {
db->arr[i].score += off;
if (db->arr[i].score < 0) db->arr[i].score = 0;
if (db->arr[i].score > 100) db->arr[i].score = 100;
}
printf("Batch bonus applied (offset=%.2f, clipped to [0,100]).\n", off);
showAll(db);
}
/* —— CSV Save/Load —— */
int saveStudentsCSV(const char *path, const StudentDB *db) {
FILE *fp = fopen(path, "w");
if (!fp) { perror("open"); return 0; }
fprintf(fp, "id,name,score\n");
for (int i = 0; i < db->count; i++)
fprintf(fp, "%d,%s,%.2f\n", db->arr[i].id, db->arr[i].name, db->arr[i].score);
fclose(fp);
return 1;
}
int loadStudentsCSV(const char *path, StudentDB *db) {
FILE *fp = fopen(path, "r");
if (!fp) { perror("open"); return 0; }
db->count = 0;
char line[256];
/* Skip header (if file has no header, delete this line) */
if (!fgets(line, sizeof(line), fp)) { fclose(fp); return 0; }
while (db->count < MAX_STU && fgets(line, sizeof(line), fp)) {
Student s;
if (sscanf(line, "%d,%19[^,],%f", &s.id, s.name, &s.score) == 3)
db->arr[db->count++] = s;
/* else: can print bad line warning */
}
fclose(fp);
return 1;
}
✅ IoT Sensor Monitoring System · Upgrade Version (New 11 Save CSV / 12 Read CSV) C Language Pointer Practical: IoT Sensor Monitoring System · Upgrade Version (Menu + Struct + Pointer + Sorting + Alarm)
#include <stdio.h>
#include <string.h>
#define MAX_SENSOR 200
typedef struct {
int id;
char type[16];
char location[32];
float value;
} Sensor;
typedef struct {
Sensor arr[MAX_SENSOR];
int count;
} SensorDB;
/* —— Function Declarations —— */
void initDB(SensorDB *db);
int menu();
void addSensors(SensorDB *db);
void showAll(const SensorDB *db);
Sensor* findById(SensorDB *db, int id);
void updateById(SensorDB *db);
void deleteById(SensorDB *db);
void filterByType(const SensorDB *db);
void filterByThreshold(const SensorDB *db);
void sortByValueDesc(SensorDB *db);
void statistics(const SensorDB *db);
void batchCalibrate(SensorDB *db);
/* —— CSV Persistence —— */
int saveSensorsCSV(const char *path, const SensorDB *db);
int loadSensorsCSV(const char *path, SensorDB *db);
/* —— Main Program —— */
int main() {
SensorDB db;
initDB(&db);
while (1) {
switch (menu()) {
case 1: addSensors(&db); break;
case 2: showAll(&db); break;
case 3: {
int id; printf("Please enter the ID to query:"); scanf("%d", &id);
Sensor *p = findById(&db, id);
if (p) printf("Found: %d %s %s %.2f\n", p->id, p->type, p->location, p->value);
else printf("ID %d not found.\n", id);
} break;
case 4: updateById(&db); break;
case 5: deleteById(&db); break;
case 6: filterByType(&db); break;
case 7: filterByThreshold(&db); break;
case 8: sortByValueDesc(&db); break;
case 9: statistics(&db); break;
case 10: batchCalibrate(&db); break;
case 11: /* Save CSV */
if (saveSensorsCSV("sensors.csv", &db))
printf("Saved to sensors.csv\n");
else
printf("Save failed\n");
break;
case 12: /* Read CSV */
if (loadSensorsCSV("sensors.csv", &db)) {
printf("Read from sensors.csv, current device count: %d\n", db.count);
showAll(&db);
} else {
printf("Read failed\n");
}
break;
case 0:
printf("System exited, goodbye!\n");
return 0;
default:
printf("Invalid option, please try again.\n");
}
}
}
/* —— Menu/Initialization —— */
void initDB(SensorDB *db) { db->count = 0; }
int menu() {
int c;
printf("\n===== IoT Sensor Monitoring System · Upgrade Version (with CSV) =====\n");
printf("1 Input 2 Display 3 Search by ID 4 Update 5 Delete\n");
printf("6 Filter by Type 7 Filter by Threshold 8 Descending Readings\n");
printf("9 Statistics 10 Batch Calibrate\n");
printf("11 Save CSV 12 Read CSV\n");
printf("0 Exit\n");
printf("=============================================
");
printf("Please enter option:");
scanf("%d", &c);
return c;
}
/* —— Business Functions (same as previous version) —— */
void addSensors(SensorDB *db) {
int n; printf("Please enter the number of sensors to input:"); scanf("%d", &n);
if (n <= 0) { puts("Number must be greater than 0."); return; }
if (db->count + n > MAX_SENSOR) { printf("Insufficient capacity, can input %d more devices.\n", MAX_SENSOR - db->count); return; }
for (int i = 0; i < n; i++) {
Sensor s;
printf("Please enter (id type location value), example: 101 Temp Room101 26.5\n");
scanf("%d %15s %31s %f", &s.id, s.type, s.location, &s.value);
db->arr[db->count++] = s;
}
printf("✅ Input completed, current device count: %d\n", db->count);
}
void showAll(const SensorDB *db) {
if (db->count == 0) { puts("No data available."); return; }
printf("\nID\tType\tLocation\t Value\n");
for (int i = 0; i < db->count; i++)
printf("%d\t%-5s\t%-16s\t%.2f\n", db->arr[i].id, db->arr[i].type, db->arr[i].location, db->arr[i].value);
}
Sensor* findById(SensorDB *db, int id) {
for (int i = 0; i < db->count; i++) if (db->arr[i].id == id) return &db->arr[i];
return NULL;
}
void updateById(SensorDB *db) {
int id; float v;
printf("Please enter ID and new reading (id value):");
scanf("%d %f", &id, &v);
Sensor *p = findById(db, id);
if (p) { p->value = v; printf("Updated: %d New Value=%.2f\n", id, p->value); }
else { printf("ID %d not found.\n", id); }
}
void deleteById(SensorDB *db) {
if (db->count == 0) { puts("No data available."); return; }
int id, pos = -1; printf("Please enter the ID to delete:"); scanf("%d", &id);
for (int i = 0; i < db->count; i++) if (db->arr[i].id == id) { pos = i; break; }
if (pos == -1) { printf("ID %d not found.\n", id); return; }
for (int i = pos; i < db->count - 1; i++) db->arr[i] = db->arr[i + 1];
db->count--; printf("Deleted, current device count: %d\n", db->count);
}
void filterByType(const SensorDB *db) {
if (db->count == 0) { puts("No data available."); return; }
char t[16]; int found = 0; printf("Please enter type (Temp/Humi/Smoke...):"); scanf("%15s", t);
printf("\n=== Devices of type %s ===\n", t);
for (int i = 0; i < db->count; i++)
if (strcmp(db->arr[i].type, t) == 0)
{ printf("%d\t%s\t%s\t%.2f\n", db->arr[i].id, db->arr[i].type, db->arr[i].location, db->arr[i].value); found = 1; }
if (!found) puts("No devices of this type found.");
}
void filterByThreshold(const SensorDB *db) {
if (db->count == 0) { puts("No data available."); return; }
float th; int found = 0; printf("Please enter threshold (show >= threshold):"); scanf("%f", &th);
printf("\n=== Devices >= %.2f ===\n", th);
for (int i = 0; i < db->count; i++)
if (db->arr[i].value >= th)
{ printf("%d\t%s\t%s\t%.2f\n", db->arr[i].id, db->arr[i].type, db->arr[i].location, db->arr[i].value); found = 1; }
if (!found) puts("No devices above threshold.");
}
void sortByValueDesc(SensorDB *db) {
if (db->count <= 1) { puts("Insufficient data, no need to sort."); return; }
for (int i = 0; i < db->count - 1; i++)
for (int j = 0; j < db->count - 1 - i; j++)
if (db->arr[j].value < db->arr[j + 1].value) {
Sensor t = db->arr[j]; db->arr[j] = db->arr[j + 1]; db->arr[j + 1] = t;
}
puts("Sorted by readings in descending order."); showAll(db);
}
void statistics(const SensorDB *db) {
if (db->count == 0) { puts("No data available."); return; }
float min=db->arr[0].value, max=db->arr[0].value, sum=0.0f;
for (int i = 0; i < db->count; i++) {
float x=db->arr[i].value; if (x<min) min=x; if (x>max) max=x; sum+=x;
}
printf("Device count: %d Minimum: %.2f Maximum: %.2f Average: %.2f\n", db->count, min, max, sum/(db->count?db->count:1));
}
void batchCalibrate(SensorDB *db) {
if (db->count == 0) { puts("No data available."); return; }
float offset; printf("Please enter calibration offset (e.g. +0.5 / -0.5):"); scanf("%f", &offset);
for (int i = 0; i < db->count; i++) db->arr[i].value += offset;
printf("Batch calibration applied (offset=%.2f).\n", offset); showAll(db);
}
/* —— CSV Save/Load —— */
int saveSensorsCSV(const char *path, const SensorDB *db) {
FILE *fp = fopen(path, "w");
if (!fp) { perror("open"); return 0; }
fprintf(fp, "id,type,location,value\n");
for (int i = 0; i < db->count; i++)
fprintf(fp, "%d,%s,%s,%.2f\n",
db->arr[i].id, db->arr[i].type, db->arr[i].location, db->arr[i].value);
fclose(fp);
return 1;
}
int loadSensorsCSV(const char *path, SensorDB *db) {
FILE *fp = fopen(path, "r");
if (!fp) { perror("open"); return 0; }
db->count = 0;
char line[256];
if (!fgets(line, sizeof(line), fp)) { fclose(fp); return 0; } /* Skip header */
while (db->count < MAX_SENSOR && fgets(line, sizeof(line), fp)) {
Sensor s;
if (sscanf(line, "%d,%15[^,],%31[^,],%f", &s.id, s.type, s.location, &s.value) == 4)
db->arr[db->count++] = s;
/* else: can print bad line warning */
}
fclose(fp);
return 1;
}
6. Classroom/Self-Learning Suggestions
- Sort before saving (e.g., ascending by ID), files are neater;
- Print the first few lines after reading back, students have feedback;
- Use UTF-8 uniformly; during the introductory phase, fields should use English and numbers as much as possible;
- Use
<span>perror</span>for error reporting, faster problem localization.