Introduction to File I/O in C: Understanding “Why Save Data” Before Using CSV to Write and Read Data

⭐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:

  1. 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.

Leave a Comment