C గ్రాండ్ మినీ ప్రాజెక్ట్: స్టూడెంట్ రికార్డ్ మేనేజ్‌మెంట్ సిస్టమ్ (CRUD & Binary Persistence)

⚡ Modern C (C99 / C11 / C17 Standards) 🏆 Chapter 20 of 20 (Capstone Project) 📂 Phase 05: స్ట్రక్చర్స్, డైనమిక్ మెమరీ & ప్రాజెక్ట్ 📅 2026 Academic Edition
📌 Covered in this capstone: సాఫ్ట్‌వేర్ ఆర్కిటెక్చర్ · CRUD ఆపరేషన్స్ · Structs & Binary Persistence · ఇన్-ప్లేస్ fseek() అప్‌డేట్ · ఫిజికల్ డిలీషన్ & కాంపాక్షన్ · qsort() మెరిట్ ర్యాంకింగ్ · CSV ఎక్స్‌పోర్ట్ · ఇన్‌పుట్ బఫర్ శానిటైజేషన్ · ప్రొడక్షన్ గ్రేడ్ ఫుల్ కోడ్

అభినందనలు C డెవలపర్! 🎓 మన 20 అధ్యాయాల ప్రయాణంలో శిఖరాగ్రానికి చేరుకున్నాం!

మొదటి అధ్యాయంలో printf("Hello, World!"); రాయడం నుండి ప్రారంభించి... వేరియబుల్స్, కండిషనల్స్, లూప్స్, ఫంక్షన్స్, రికార్షన్, అర్రేలు, స్ట్రింగ్స్, పాయింటర్లు, డబుల్ పాయింటర్లు, స్ట్రక్చర్లు, డైనమిక్ మెమరీ (DMA), మరియు బైనరీ ఫైల్ హ్యాండ్లింగ్ వరకు ప్రతి కాన్సెప్ట్‌ను లోతుగా అన్వేషించాం.

ఇప్పుడు వీటన్నింటినీ ఒకచోట చేర్చి ఒక వాస్తవ ప్రపంచ, ఇండస్ట్రీ-స్టాండర్డ్ స్టూడెంట్ రికార్డ్ మేనేజ్‌మెంట్ సిస్టమ్ (Student Record Management CLI System) ను మొదటి నుండి చివరి వరకు నిర్మిద్దాం. ఇది కేవలం ఒక చిన్న డెమో కాదు—పూర్తి స్థాయి CRUD ఆపరేషన్లు, బైనరీ ఫైల్ పర్సిస్టెన్స్, రాండమ్-యాక్సెస్ అప్‌డేట్స్, ఆటోమేటిక్ మెరిట్ ర్యాంకింగ్ మరియు ఎక్సెల్ CSV ఎక్స్‌పోర్ట్‌తో కూడిన ఒక పూర్తి ఉత్పత్తి!

1 ప్రాజెక్ట్ అవలోకనం & సాఫ్ట్‌వేర్ ఆర్కిటెక్చర్ (System Architecture)

ఒక సాఫ్ట్‌వేర్ ప్రాజెక్ట్ కోడింగ్ ప్రారంభించే ముందు, దాని ఆర్కిటెక్చర్ మరియు డేటా ప్రవాహాన్ని డిజైన్ చేసుకోవడం అత్యంత ఉత్తమ ఇంజనీరింగ్ పద్ధతి. మన స్టూడెంట్ రికార్డ్ మేనేజర్ CRUD (Create, Read, Update, Delete) మోడల్‌పై ఆధారపడి పనిచేస్తుంది:

CRUD ఆపరేషన్ సిస్టమ్ ఫంక్షన్ ఫైల్ మోడ్ సాంకేతిక వివరణ
C - Create add_student() "ab" (Append Binary) యూజర్ వివరాలను చదివి, CGPA లెక్కించి, బైనరీ రికార్డును ఫైల్ చివరన శాశ్వతంగా రాస్తుంది.
R - Read view_all() / search() "rb" (Read Binary) ఫైల్ నుండి డేటాను చదివి చక్కటి టేబులర్ ఫార్మాట్‌లో కన్సోల్ పై ప్రదర్శిస్తుంది.
U - Update update_student() "rb+" (Read/Write Binary) fseek() ద్వారా నిర్దిష్ట రికార్డును గుర్తించి ఇన్-ప్లేస్ (అక్కడికక్కడే) సవరిస్తుంది.
D - Delete delete_student() "rb" & "wb" టెంపరరీ ఫైల్ టెక్నిక్ ద్వారా తొలగించాల్సిన రికార్డును మినహాయించి డేటాబేస్‌ను రీరైట్ చేస్తుంది.

సిస్టమ్ డేటా ఫ్లో ఆర్కిటెక్చర్:

[ User Terminal / Console UI ]
│
▼ (Menu Selection: 1 to 8)
[ Input Sanitation & Validation Layer ] <--- బఫర్ క్లియరింగ్, రేంజ్ చెక్స్
│
▼ (Business Logic & Struct Processing)
[ Memory Engine: Student Struct / Heap Buffer ]
│
▼ (fread / fwrite / fseek)
[ Binary Persistence Engine: "students.dat" on Disk ]
│
▼ (Optional Export)
[ Spreadsheet Ready CSV: "students_export.csv" ]

2 డేటా స్ట్రక్చర్ డిజైన్: typedef struct మరియు డొమైన్ మోడలింగ్

మన డేటాబేస్ యొక్క గుండెకాయ Student స్ట్రక్చర్. విద్యార్థి వ్యక్తిగత సమాచారం, అకడమిక్ మార్కులు మరియు సిస్టమ్ స్టేటస్ అన్నీ ఇందులో సమగ్రంగా భద్రపరచబడతాయి:

C — Student Struct Definition & Domain Modeling
#define MAX_NAME 50
#define MAX_COURSE 30
#define NUM_SUBJECTS 5
#define DB_FILE "students.dat"
#define CSV_FILE "students_export.csv"

typedef struct {
    int roll_no;                    // ప్రత్యేక రోల్ నంబర్ (Primary Key)
    char name[MAX_NAME];            // విద్యార్థి పూర్తి పేరు
    char course[MAX_COURSE];        // బ్రాంచ్ / కోర్సు (ఉదా: CSE, ECE)
    float marks[NUM_SUBJECTS];      // 5 సబ్జెక్టుల మార్కులు
    float total_marks;              // మొత్తం మార్కులు
    float cgpa;                     // CGPA (స్కేల్: 10.0)
    char grade[4];                  // లెటర్ గ్రేడ్ (A+, A, B, etc.)
} Student;
💡 ఫిక్స్డ్ సైజ్ అర్రేల ప్రాముఖ్యత:
బైనరీ ఫైల్ హ్యాండ్లింగ్‌లో స్ట్రక్చర్ లోపల డైనమిక్ పాయింటర్లు (ఉదా: char *name) వాడకూడదు! ఎందుకంటే పాయింటర్ కేవలం RAM అడ్రస్‌ను మాత్రమే డిస్క్‌పై రాస్తుంది (డేటాను కాదు). ఫిక్స్డ్ సైజ్ అర్రేలు (char name[50]) వాడినప్పుడు మాత్రమే ప్రతి రికార్డు ఖచ్చితమైన sizeof(Student) బైట్లతో డిస్క్‌పై నిల్వ అవుతుంది.

3 మోడ్యులర్ కోడ్ ఆర్కిటెక్చర్ & ఫంక్షన్ ప్రోటోటైప్స్

ఒక పెద్ద ప్రాజెక్ట్‌ను రాసేటప్పుడు మొత్తం కోడ్‌ను main() లో గుమ్మరించకూడదు! సింగిల్ రెస్పాన్సిబిలిటీ ప్రిన్సిపల్ (Single Responsibility Principle) ప్రకారం ప్రతి బాధ్యతకు ఒక ప్రత్యేక ఫంక్షన్ ఉండాలి:

  • void display_menu(void); — ప్రధాన మెనూ ఇంటర్‌ఫేస్.
  • void add_student(void); — కొత్త రికార్డు నమోదు & డూప్లికేట్ వెరిఫికేషన్.
  • void view_all_students(void); — మొత్తం విద్యార్థుల టేబులర్ లిస్టింగ్.
  • void search_student(void); — రోల్ నంబర్ లేదా పేరుతో శోధన.
  • void update_student(void); — మార్కులు లేదా కోర్సు సవరణ.
  • void delete_student(void); — రికార్డును శాశ్వతంగా తొలగించడం.
  • void show_merit_list(void); — CGPA ఆధారంగా టాప్ ర్యాంకర్ల సార్టింగ్.
  • void export_to_csv(void); — ఎక్సెల్ షీట్‌లోకి ఎగుమతి.
  • void clear_input_buffer(void); — ఇన్‌పుట్ బఫర్ శానిటైజేషన్.

4 ఇన్‌పుట్ శానిటైజేషన్, బఫర్ క్లియరింగ్ & వాలిడేషన్

C ప్రోగ్రామింగ్‌లో అత్యంత సాధారణమైన క్రాష్: యూజర్ నుండి సంఖ్యను రీడ్ చేయడానికి scanf("%d", ...) వాడి, ఆ తర్వాత స్ట్రింగ్ చదవడానికి fgets() వాడినప్పుడు జరుగుతుంది. scanf సంఖ్యను తీసుకుంటుంది కానీ యూజర్ నొక్కిన Enter key ('\n') ను ఇన్‌పుట్ బఫర్‌లోనే వదిలేస్తుంది! దానివల్ల తర్వాతి fgets() ఏమీ చదవకుండానే ఖాళీగా స్కిప్ అయిపోతుంది!

పర్ఫెక్ట్ బఫర్ క్లీనర్ యుటిలిటీ:

C — Buffer Sanitation & Trailing Newline Trimming
// కీబోర్డ్ ఇన్‌పుట్ బఫర్‌లో మిగిలిపోయిన న్యూలైన్ మరియు అదనపు క్యారెక్టర్లను తుడిచిపెట్టే ఫంక్షన్
void clear_input_buffer() {
    int c;
    while ((c = getchar()) != '\n' && c != EOF);
}

// fgets నుండి వచ్చే చివరి '\n' ను సురక్షితంగా తొలగించడం
void trim_newline(char *str) {
    size_t len = strlen(str);
    if (len > 0 && str[len - 1] == '\n') {
        str[len - 1] = '\0';
    }
}

5 Create (C) ఇంప్లిమెంటేషన్: డూప్లికేట్ రోల్ నంబర్ చెకింగ్ & బైనరీ అపెండ్

ఒక విద్యార్థిని జోడించే ముందు మనం రెండు ముఖ్యమైన చెక్స్ చేయాలి:

  1. ఆ రోల్ నంబర్ ఇప్పటికే డేటాబేస్‌లో ఉందా లేదా పరిశీలించాలి (Primary Key Uniqueness).
  2. మార్కులు 0 నుండి 100 మధ్య మాత్రమే ఉండేలా ధృవీకరించాలి.

ఆ తర్వాత 5 సబ్జెక్టుల మార్కుల సగటు ఆధారంగా CGPA = Total / 50.0 మరియు లెటర్ గ్రేడ్ (O, A+, A, B+, B, C, F) ఆటోమేటిక్‌గా లెక్కించబడి fwrite() ద్వారా "ab" మోడ్‌లో భద్రపరచబడుతుంది.

6 Read (R) ఇంప్లిమెంటేషన్: ఫార్మాటెడ్ టేబుల్ రిపోర్ట్ & సెర్చ్ అల్గారిథమ్స్

డేటాబేస్‌లోని రికార్డులను సాధారణ టెక్స్ట్‌లా కాకుండా, ఒక ప్రొఫెషనల్ అడ్మిన్ డ్యాష్‌బోర్డ్‌లా కాలమ్ హెడర్లు, బోర్డర్లు మరియు చక్కటి అమరికతో ప్రదర్శించాలి.

శోధన ఫీచర్ (Search) లో రెండు ఆప్షన్లు ఉంటాయి:

  • రోల్ నంబర్ ద్వారా శోధన: ఖచ్చితమైన విద్యార్థిని సెకన్లలో గుర్తిస్తుంది.
  • పేరు ద్వారా శోధన (Substring Search): strstr() ద్వారా పేరులోని ఏ భాగాన్నైనా (ఉదా: "వర్మ" లేదా "శర్మ") గుర్తించి చూపిస్తుంది.

7 Update (U) ఇంప్లిమెంటేషన్: fseek() ద్వారా ఇన్-ప్లేస్ బైనరీ రికార్డ్ సవరణ

రికార్డును సవరించడానికి చాలామంది బిగినర్లు ఫైల్ మొత్తాన్ని మెమరీలోకి తెచ్చి, మార్చి, మళ్లీ మొత్తం ఫైల్‌ను మొదటినుండి రీరైట్ చేస్తారు. కానీ రికార్డులు లక్షల్లో ఉంటే ఇది చాలా నెమ్మదిగా మారుతుంది.

C భాషలో "rb+" (Read and Write Binary) మోడ్ మరియు fseek() కాంబినేషన్‌తో మనం సరిగ్గా ఆ ఒక్క రికార్డు స్థానానికి వెళ్లి, అక్కడికక్కడే (In-Place) ఓవర్‌రైట్ చేయవచ్చు!

// ఇన్-ప్లేస్ అప్‌డేట్ మ్యాజిక్ లాజిక్:
while (fread(&s, sizeof(Student), 1, fp) == 1) {
if (s.roll_no == target_roll) {
// 1. fread కర్సర్‌ను ఒక రికార్డు ముందుకు తీసుకెళ్లింది!
// 2. కాబట్టి కరెక్ట్ గా ఒక రికార్డు వెనక్కి జంప్ చేయాలి:
fseek(fp, -((long)sizeof(Student)), SEEK_CUR);
// 3. ఇప్పుడే కొత్త రికార్డును ఓవర్‌రైట్ చేయాలి:
fwrite(&updated_s, sizeof(Student), 1, fp);
break;
}
}

8 Delete (D) ఇంప్లిమెంటేషన్: ఫిజికల్ డిలీషన్ & ఫైల్ కాంపాక్షన్ టెక్నిక్

ఒక బైనరీ ఫైల్ మధ్యలో ఉన్న డేటాను నేరుగా తొలగించి ఖాళీని పూడ్చడం హార్డ్ డిస్క్‌లలో సాధ్యపడదు. దీనికి ఇండస్ట్రీలో ప్రామాణికంగా వాడే విధానం తాత్కాలిక ఫైల్ (Temporary File Compaction) టెక్నిక్:

  1. ప్రధాన ఫైల్ "students.dat" ను రీడ్ మోడ్‌లో తెరవడం.
  2. కొత్తగా "temp.dat" అనే తాత్కాలిక ఫైల్‌ను రైట్ మోడ్‌లో సృష్టించడం.
  3. మనం డిలీట్ చేయాలనుకున్న రోల్ నంబర్ మినహా మిగిలిన అన్ని రికార్డులను temp.dat లోకి కాపీ చేయడం.
  4. రెండు ఫైల్స్‌నూ క్లోజ్ చేయడం.
  5. remove("students.dat"); ద్వారా పాత ఫైల్‌ను డిలీట్ చేయడం.
  6. rename("temp.dat", "students.dat"); ద్వారా తాత్కాలిక ఫైల్‌ను ప్రధాన ఫైల్‌గా మార్చడం!

9 అడ్వాన్స్‌డ్ ఫీచర్: మెరిట్ ర్యాంకింగ్ & qsort() అల్గారిథమ్

విద్యార్థుల డేటాబేస్ అంటే కేవలం స్టోర్ చేయడం మాత్రమే కాదు—తరగతిలో అత్యధిక మార్కులు సాధించిన టాప్ ర్యాంకర్లను గుర్తించడం అత్యంత ఆకర్షణీయమైన ఫీచర్.

దీనికోసం మనం ఫైల్‌లోని మొత్తం రికార్డుల సంఖ్యను లెక్కించి, malloc() ద్వారా డైనమిక్ మెమరీని రిజర్వ్ చేసి, C స్టాండర్డ్ లైబ్రరీలోని హై-స్పీడ్ qsort() (Quicksort) అల్గారిథమ్‌తో CGPA ఆధారంగా డిసెండింగ్ ఆర్డర్‌లో క్రమబద్ధీకరిస్తాం!

C — Merit Ranking Comparator for qsort()
// qsort కోసం CGPA డిసెండింగ్ కంపేరేటర్ ఫంక్షన్
int compare_students_by_cgpa(const void *a, const void *b) {
    const Student *s1 = (const Student *)a;
    const Student *s2 = (const Student *)b;
    if (s2->cgpa > s1->cgpa) return 1;
    if (s2->cgpa < s1->cgpa) return -1;
    return 0;
}

10 డేటాబేస్ ఎక్స్‌పోర్ట్ టు CSV (Excel Spreadsheet Integration)

మన ప్రోగ్రామ్ బైనరీ ఫైల్ వాడుతుంది కాబట్టి సాధారణ వినియోగదారులు ఆ ఫైల్‌ను నేరుగా చూడలేరు. కానీ కళాశాల యాజమాన్యం ఆ డేటాను Microsoft Excel లేదా Google Sheets లో చూడాలనుకోవచ్చు!

ఈ ఫీచర్ ద్వారా మన C ప్రోగ్రామ్ బైనరీ ఫైల్‌లోని ప్రతి రికార్డును చదివి, కామాలతో వేరుచేయబడిన students_export.csv (Comma-Separated Values) ఫైల్‌గా ఎగుమతి చేస్తుంది. ఎక్సెల్‌లో దీనిపై డబుల్ క్లిక్ చేయగానే అందమైన స్ప్రెడ్‌షీట్‌గా ఓపెన్ అవుతుంది!

11 కంప్లీట్ ప్రొడక్షన్-గ్రేడ్ సోర్స్ కోడ్ (Single File Executable Masterpiece)

ఇదిగోండి మన 20 అధ్యాయాల సమగ్ర C మాస్టర్‌క్లాస్ యొక్క పూర్తి ముగింపు ప్రాజెక్ట్ సోర్స్ కోడ్! దీనిని కాపీ చేసి రన్ చేయండి:

C — Complete Student Record Management Capstone CLI System
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NAME 50
#define MAX_COURSE 30
#define NUM_SUBJECTS 5
#define DB_FILE "students.dat"
#define CSV_FILE "students_export.csv"

// విద్యార్థి సమాచార స్ట్రక్చర్
typedef struct {
    int roll_no;
    char name[MAX_NAME];
    char course[MAX_COURSE];
    float marks[NUM_SUBJECTS];
    float total_marks;
    float cgpa;
    char grade[4];
} Student;

// యుటిలిటీ ఫంక్షన్లు
void clear_input_buffer() {
    int c;
    while ((c = getchar()) != '\n' && c != EOF);
}

void trim_newline(char *str) {
    size_t len = strlen(str);
    if (len > 0 && str[len - 1] == '\n') {
        str[len - 1] = '\0';
    }
}

// గ్రేడ్ మరియు CGPA లెక్కింపు
void calculate_academics(Student *s) {
    s->total_marks = 0;
    for (int i = 0; i < NUM_SUBJECTS; i++) {
        s->total_marks += s->marks[i];
    }
    // గరిష్ట మార్కులు 500 అనుకుంటే, 10 పాయింట్ స్కేల్:
    s->cgpa = s->total_marks / 50.0f;

    if (s->cgpa >= 9.0f) strcpy(s->grade, "O");
    else if (s->cgpa >= 8.0f) strcpy(s->grade, "A+");
    else if (s->cgpa >= 7.0f) strcpy(s->grade, "A");
    else if (s->cgpa >= 6.0f) strcpy(s->grade, "B+");
    else if (s->cgpa >= 5.0f) strcpy(s->grade, "B");
    else if (s->cgpa >= 4.0f) strcpy(s->grade, "C");
    else strcpy(s->grade, "F");
}

// రోల్ నంబర్ డూప్లికేట్ ఉందో లేదో తనిఖీ
int check_roll_exists(int roll) {
    FILE *fp = fopen(DB_FILE, "rb");
    if (!fp) return 0;

    Student s;
    while (fread(&s, sizeof(Student), 1, fp) == 1) {
        if (s.roll_no == roll) {
            fclose(fp);
            return 1;
        }
    }
    fclose(fp);
    return 0;
}

// 1. కొత్త విద్యార్థిని చేర్చడం (Create)
void add_student() {
    Student s;
    printf("\n--- కొత్త విద్యార్థి నమోదు ---\n");
    printf("రోల్ నంబర్ ఎంటర్ చేయండి: ");
    if (scanf("%d", &s.roll_no) != 1 || s.roll_no <= 0) {
        printf("❌ చెల్లని రోల్ నంబర్!\n");
        clear_input_buffer();
        return;
    }
    clear_input_buffer();

    if (check_roll_exists(s.roll_no)) {
        printf("⚠️ ఎర్రర్: రోల్ నంబర్ %d తో ఇప్పటికే ఒక రికార్డు ఉంది!\n", s.roll_no);
        return;
    }

    printf("పూర్తి పేరు: ");
    if (fgets(s.name, sizeof(s.name), stdin)) trim_newline(s.name);

    printf("కోర్సు / బ్రాంచ్ (ఉదా: CSE, ECE): ");
    if (fgets(s.course, sizeof(s.course), stdin)) trim_newline(s.course);

    printf("%d సబ్జెక్టుల మార్కులు ఎంటర్ చేయండి (0 - 100):\n", NUM_SUBJECTS);
    for (int i = 0; i < NUM_SUBJECTS; i++) {
        while (1) {
            printf("  సబ్జెక్ట్ %d మార్కులు: ", i + 1);
            if (scanf("%f", &s.marks[i]) == 1 && s.marks[i] >= 0.0f && s.marks[i] <= 100.0f) {
                break;
            }
            printf("  ❌ దయచేసి 0 నుండి 100 మధ్య మాత్రమే ఇవ్వండి!\n");
            clear_input_buffer();
        }
    }
    clear_input_buffer();

    calculate_academics(&s);

    FILE *fp = fopen(DB_FILE, "ab");
    if (!fp) {
        perror("డేటాబేస్ ఫైల్ ఓపెన్ విఫలమైంది");
        return;
    }
    fwrite(&s, sizeof(Student), 1, fp);
    fclose(fp);

    printf("✅ విద్యార్థి %s విజయవంతంగా సేవ్ అయ్యారు! (CGPA: %.2f | గ్రేడ్: %s)\n", s.name, s.cgpa, s.grade);
}

// 2. అందరు విద్యార్థులను చూపించడం (Read)
void view_all_students() {
    FILE *fp = fopen(DB_FILE, "rb");
    if (!fp) {
        printf("\n⚠️ డేటాబేస్ ఖాళీగా ఉంది లేదా ఫైల్ లేదు!\n");
        return;
    }

    Student s;
    printf("\n========================================================================================\n");
    printf("%-8s %-22s %-12s %-8s %-8s %-6s\n", "రోల్ నం", "పేరు", "కోర్సు", "మొత్తం", "CGPA", "గ్రేడ్");
    printf("========================================================================================\n");

    int count = 0;
    while (fread(&s, sizeof(Student), 1, fp) == 1) {
        printf("%-8d %-22s %-12s %-8.1f %-8.2f %-6s\n", 
               s.roll_no, s.name, s.course, s.total_marks, s.cgpa, s.grade);
        count++;
    }
    printf("----------------------------------------------------------------------------------------\n");
    printf("మొత్తం విద్యార్థుల సంఖ్య: %d\n\n", count);
    fclose(fp);
}

// 3. శోధన (Search)
void search_student() {
    FILE *fp = fopen(DB_FILE, "rb");
    if (!fp) {
        printf("డేటాబేస్ అందుబాటులో లేదు.\n");
        return;
    }

    printf("\nశోధన పద్ధతి ఎంచుకోండి:\n1. రోల్ నంబర్ ద్వారా\n2. పేరు ద్వారా\nఎంపిక: ");
    int choice;
    if (scanf("%d", &choice) != 1) { clear_input_buffer(); fclose(fp); return; }
    clear_input_buffer();

    Student s;
    int found = 0;

    if (choice == 1) {
        int roll;
        printf("వెతకాల్సిన రోల్ నంబర్: ");
        scanf("%d", &roll);
        clear_input_buffer();

        while (fread(&s, sizeof(Student), 1, fp) == 1) {
            if (s.roll_no == roll) {
                printf("\n🔍 విద్యార్థి వివరాలు లభించాయి:\n");
                printf("రోల్ నంబర్ : %d\nపేరు       : %s\nకోర్సు      : %s\nCGPA       : %.2f\nగ్రేడ్     : %s\n", 
                       s.roll_no, s.name, s.course, s.cgpa, s.grade);
                found = 1;
                break;
            }
        }
    } else if (choice == 2) {
        char name[MAX_NAME];
        printf("వెతకాల్సిన పేరు / భాగం: ");
        fgets(name, sizeof(name), stdin);
        trim_newline(name);

        while (fread(&s, sizeof(Student), 1, fp) == 1) {
            if (strstr(s.name, name) != NULL) {
                printf("👉 [రోల్: %d] %-20s | కోర్సు: %-8s | CGPA: %.2f | గ్రేడ్: %s\n", 
                       s.roll_no, s.name, s.course, s.cgpa, s.grade);
                found = 1;
            }
        }
    }

    if (!found) printf("❌ ఎలాంటి సరిపోలే రికార్డు కనుగొనబడలేదు.\n");
    fclose(fp);
}

// 4. రికార్డు సవరణ (Update - In-Place)
void update_student() {
    FILE *fp = fopen(DB_FILE, "rb+");
    if (!fp) {
        printf("డేటాబేస్ అందుబాటులో లేదు.\n");
        return;
    }

    int roll;
    printf("\nసవరించాల్సిన విద్యార్థి రోల్ నంబర్: ");
    if (scanf("%d", &roll) != 1) { clear_input_buffer(); fclose(fp); return; }
    clear_input_buffer();

    Student s;
    int found = 0;

    while (fread(&s, sizeof(Student), 1, fp) == 1) {
        if (s.roll_no == roll) {
            found = 1;
            printf("ప్రస్తుత పేరు: %s | కోర్సు: %s | CGPA: %.2f\n", s.name, s.course, s.cgpa);
            
            printf("కొత్త పేరు (మార్పు లేకపోతే Enter నొక్కండి): ");
            char new_name[MAX_NAME];
            fgets(new_name, sizeof(new_name), stdin);
            trim_newline(new_name);
            if (strlen(new_name) > 0) strcpy(s.name, new_name);

            printf("కొత్త కోర్సు (మార్పు లేకపోతే Enter నొక్కండి): ");
            char new_course[MAX_COURSE];
            fgets(new_course, sizeof(new_course), stdin);
            trim_newline(new_course);
            if (strlen(new_course) > 0) strcpy(s.name, new_course);

            // మార్కులు మళ్లీ ఎంటర్ చేయాలా?
            printf("సబ్జెక్ట్ మార్కులు అప్‌డేట్ చేయాలా? (1 = అవును, 0 = కాదు): ");
            int ch_marks;
            if (scanf("%d", &ch_marks) == 1 && ch_marks == 1) {
                for (int i = 0; i < NUM_SUBJECTS; i++) {
                    printf("  సబ్జెక్ట్ %d కొత్త మార్కులు: ", i + 1);
                    scanf("%f", &s.marks[i]);
                }
                calculate_academics(&s);
            }
            clear_input_buffer();

            // ఇన్-ప్లేస్ బ్యాక్‌ట్రాక్ మరియు ఓవర్‌రైట్
            fseek(fp, -((long)sizeof(Student)), SEEK_CUR);
            fwrite(&s, sizeof(Student), 1, fp);
            printf("✅ రికార్డు విజయవంతంగా అప్‌డేట్ చేయబడింది!\n");
            break;
        }
    }

    if (!found) printf("❌ రోల్ నంబర్ %d తో రికార్డు లభించలేదు.\n", roll);
    fclose(fp);
}

// 5. రికార్డు తొలగింపు (Delete - File Compaction)
void delete_student() {
    int roll;
    printf("\nతొలగించాల్సిన విద్యార్థి రోల్ నంబర్: ");
    if (scanf("%d", &roll) != 1) { clear_input_buffer(); return; }
    clear_input_buffer();

    FILE *fp = fopen(DB_FILE, "rb");
    if (!fp) { printf("డేటాబేస్ లేదు.\n"); return; }

    FILE *temp = fopen("temp.dat", "wb");
    if (!temp) { perror("టెంప్ ఫైల్ క్రియేషన్ విఫలం"); fclose(fp); return; }

    Student s;
    int deleted = 0;

    while (fread(&s, sizeof(Student), 1, fp) == 1) {
        if (s.roll_no == roll) {
            deleted = 1;
            printf("🗑️ రికార్డు తొలగించబడుతోంది: %s (రోల్: %d)\n", s.name, s.roll_no);
        } else {
            fwrite(&s, sizeof(Student), 1, temp);
        }
    }

    fclose(fp);
    fclose(temp);

    if (deleted) {
        remove(DB_FILE);
        rename("temp.dat", DB_FILE);
        printf("✅ రికార్డు శాశ్వతంగా తొలగించబడింది!\n");
    } else {
        remove("temp.dat");
        printf("❌ రోల్ నంబర్ %d కనుగొనబడలేదు.\n", roll);
    }
}

// 6. మెరిట్ లిస్ట్ & qsort ర్యాంకింగ్
int compare_cgpa(const void *a, const void *b) {
    const Student *s1 = (const Student *)a;
    const Student *s2 = (const Student *)b;
    if (s2->cgpa > s1->cgpa) return 1;
    if (s2->cgpa < s1->cgpa) return -1;
    return 0;
}

void show_merit_list() {
    FILE *fp = fopen(DB_FILE, "rb");
    if (!fp) { printf("డేటాబేస్ ఖాళీగా ఉంది.\n"); return; }

    // మొత్తం విద్యార్థుల సంఖ్యను లెక్కించడం
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    rewind(fp);

    int count = file_size / sizeof(Student);
    if (count == 0) { printf("రికార్డులు లేవు.\n"); fclose(fp); return; }

    // హీప్ మెమరీలో డైనమిక్ కేటాయింపు
    Student *list = (Student *)malloc(count * sizeof(Student));
    if (!list) { printf("మెమరీ కేటాయింపు విఫలమైంది.\n"); fclose(fp); return; }

    fread(list, sizeof(Student), count, fp);
    fclose(fp);

    // qsort తో సార్టింగ్
    qsort(list, count, sizeof(Student), compare_cgpa);

    printf("\n🏆 ======================== కాలేజ్ మెరిట్ లిస్ట్ (TOP RANKERS) ========================\n");
    printf("%-6s %-8s %-22s %-12s %-8s %-6s\n", "ర్యాంక్", "రోల్ నం", "పేరు", "కోర్సు", "CGPA", "గ్రేడ్");
    printf("------------------------------------------------------------------------------------\n");
    for (int i = 0; i < count; i++) {
        printf("#%-5d %-8d %-22s %-12s %-8.2f %-6s\n", 
               i + 1, list[i].roll_no, list[i].name, list[i].course, list[i].cgpa, list[i].grade);
    }
    printf("====================================================================================\n\n");

    free(list);
    list = NULL;
}

// 7. CSV ఎక్స్‌పోర్ట్
void export_to_csv() {
    FILE *src = fopen(DB_FILE, "rb");
    if (!src) { printf("డేటాబేస్ లేదు.\n"); return; }

    FILE *dest = fopen(CSV_FILE, "w");
    if (!dest) { perror("CSV సృష్టి విఫలం"); fclose(src); return; }

    fprintf(dest, "Roll_No,Name,Course,Total_Marks,CGPA,Grade\n");

    Student s;
    int count = 0;
    while (fread(&s, sizeof(Student), 1, src) == 1) {
        fprintf(dest, "%d,\"%s\",\"%s\",%.2f,%.2f,%s\n", 
                s.roll_no, s.name, s.course, s.total_marks, s.cgpa, s.grade);
        count++;
    }

    fclose(src);
    fclose(dest);

    printf("📊 అద్భుతం! మొత్తం %d రికార్డులు '%s' లోకి ఎక్స్‌పోర్ట్ అయ్యాయి!\n", count, CSV_FILE);
    printf("సూచన: మీరు ఈ ఫైల్‌ను నేరుగా Microsoft Excel లేదా Google Sheets లో ఓపెన్ చేయవచ్చు.\n");
}

int main() {
    int choice;
    while (1) {
        printf("\n=======================================================\n");
        printf("       🎓 స్టూడెంట్ రికార్డ్ మేనేజ్‌మెంట్ సిస్టమ్        \n");
        printf("=======================================================\n");
        printf(" 1. ➕ కొత్త విద్యార్థిని జోడించండి (Add Student)\n");
        printf(" 2. 📋 అందరు విద్యార్థుల జాబితా చూడండి (View All)\n");
        printf(" 3. 🔍 విద్యార్థిని శోధించండి (Search Student)\n");
        printf(" 4. ✏️  రికార్డును సవరించండి (Update Record)\n");
        printf(" 5. 🗑️  రికార్డును తొలగించండి (Delete Record)\n");
        printf(" 6. 🏆 టాప్ ర్యాంకర్ల మెరిట్ లిస్ట్ (Merit Ranking)\n");
        printf(" 7. 📊 Excel CSV లోకి ఎక్స్‌పోర్ట్ చేయండి (Export CSV)\n");
        printf(" 8. 🚪 నిష్క్రమించండి (Exit)\n");
        printf("=======================================================\n");
        printf("మీ ఎంపిక (1 - 8): ");

        if (scanf("%d", &choice) != 1) {
            printf("❌ దయచేసి సరైన సంఖ్యను ఎంటర్ చేయండి!\n");
            clear_input_buffer();
            continue;
        }

        switch (choice) {
            case 1: add_student(); break;
            case 2: view_all_students(); break;
            case 3: search_student(); break;
            case 4: update_student(); break;
            case 5: delete_student(); break;
            case 6: show_merit_list(); break;
            case 7: export_to_csv(); break;
            case 8:
                printf("\nధన్యవాదాలు! ప్రోగ్రామ్ ముగిసింది. మీ C ప్రయాణం విజయవంతం కావాలి! 🚀\n");
                exit(0);
            default:
                printf("❌ చెల్లని ఎంపిక! దయచేసి 1 నుండి 8 మధ్య ఎంచుకోండి.\n");
        }
    }
    return 0;
}

🚀 లైవ్ C కంపైలర్‌లో ఈ గ్రాండ్ ప్రాజెక్ట్ రన్ చేయండి!

పూర్తి CRUD, మెరిట్ లిస్ట్ మరియు CSV ఎక్స్‌పోర్ట్ ఫీచర్లను మన ఆన్‌లైన్ కంపైలర్‌లో రన్ చేసి స్వయంగా పరీక్షించండి!

Online C Compiler ఓపెన్ చేయండి ➔

12 కంపైలేషన్, మేక్ ఫైల్ & ప్రొడక్షన్ డిప్లాయ్మెంట్ గైడ్

ఈ ప్రాజెక్ట్‌ను ప్రొఫెషనల్ సాఫ్ట్‌వేర్ ఇంజనీర్‌లా కంపైల్ చేయడానికి మరియు బిల్డ్ చేయడానికి క్రింది టూల్స్ వాడతాం:

1. GCC కంపైలేషన్ స్ట్రిక్ట్ ఫ్లాగ్స్:

gcc -Wall -Wextra -pedantic -std=c17 -O2 student_manager.c -o student_manager
  • -Wall -Wextra: సంభావ్య బగ్స్ మరియు హెచ్చరికలన్నింటినీ చూపిస్తుంది.
  • -std=c17: సరికొత్త ఆధునిక C17 ప్రమాణాలను అమలు చేస్తుంది.
  • -O2: CPU కోడ్ ఎగ్జిక్యూషన్‌ను ఆప్టిమైజ్ చేస్తుంది.

2. ప్రాజెక్ట్ Makefile నిర్మాణం:

CC = gcc
CFLAGS = -Wall -Wextra -std=c17 -O2
TARGET = student_manager

all: $(TARGET)

$(TARGET): student_manager.c
$(CC) $(CFLAGS) student_manager.c -o $(TARGET)

run: $(TARGET)
./$(TARGET)

clean:
rm -f $(TARGET) *.dat *.csv

13 కోర్సు గ్రాండ్ సారాంశం, కెరీర్ రోడ్‌మ్యాప్ & ఫైనల్ క్విజ్

మనం పూర్తి చేసిన 20 అధ్యాయాల సారాంశం:

  • Phase 01 (Ch 01-04): C ఫౌండేషన్స్, మెమరీ మోడల్, ఆపరేటర్లు & ఫార్మాట్ స్పెసిఫైయర్లు.
  • Phase 02 (Ch 05-08): బ్రాంచింగ్, జంప్ టేబుల్స్, నెస్టెడ్ లూప్స్ & ప్యాటర్న్స్.
  • Phase 03 (Ch 09-12): ఫంక్షన్స్, కాల్ బై రిఫరెన్స్, స్టోరేజ్ క్లాసెస్ & రికార్షన్ కాల్ స్టాక్.
  • Phase 04 (Ch 13-16): మల్టీ-డైమెన్షనల్ అర్రేలు, స్ట్రింగ్ సెక్యూరిటీ, పాయింటర్ అరిథ్‌మెటిక్ & డబుల్ పాయింటర్లు.
  • Phase 05 (Ch 17-20): స్ట్రక్చర్ పాడింగ్, యూనియన్స్, హీప్ DMA (malloc/free), బైనరీ ఫైల్ పర్సిస్టెన్స్ & గ్రాండ్ ప్రాజెక్ట్!

తర్వాత ఏమి నేర్చుకోవాలి? (Next Steps in Your Career Roadmap):

  1. Data Structures & Algorithms (DSA) in C: లింక్డ్ లిస్ట్‌లు, స్టాక్‌లు, క్యూలు, బైనరీ ట్రీలు మరియు గ్రాఫ్స్.
  2. Operating Systems & POSIX: Linux System Calls, ఫైల్ డిస్క్రిప్టర్లు, Fork, Threads (pthreads), Inter-Process Communication (IPC).
  3. Embedded Systems: మైక్రోకంట్రోలర్లు (Arduino, STM32, ESP32) లో బేర్-మెటల్ C ప్రోగ్రామింగ్.
  4. Modern C++: C బేస్ తర్వాత ఆబ్జెక్ట్ ఓరియెంటెడ్ ప్రోగ్రామింగ్, టెంప్లేట్లు మరియు STL.

ఫైనల్ మాస్టర్‌క్లాస్ సర్టిఫికేషన్ క్విజ్ (Capstone Quiz):

❓ ప్రశ్న 1: బైనరీ ఫైల్‌లో స్ట్రక్చర్ రికార్డులను సేవ్ చేసేటప్పుడు char *name; అని ఎందుకు వాడకూడదు?
సమాధానం: పాయింటర్ కేవలం RAM చిరునామాను మాత్రమే సూచిస్తుంది. fwrite() ఆ 8-బైట్ అడ్రస్‌ను మాత్రమే డిస్క్‌పై రాస్తుంది తప్ప అసలు స్ట్రింగ్ డేటాను రాయదు! ప్రోగ్రామ్ రీస్టార్ట్ అవ్వగానే ఆ పాత అడ్రస్ చెల్లదు. అందుకే ఫిక్స్డ్ సైజ్ అర్రే char name[50]; మాత్రమే వాడాలి.
❓ ప్రశ్న 2: fseek(fp, -((long)sizeof(Student)), SEEK_CUR); స్టేట్‌మెంట్ ఏం చేస్తుంది?
సమాధానం: fread() చేసిన వెంటనే ఫైల్ కర్సర్ ఒక రికార్డు ముందుకు వెళ్లి ఉంటుంది. ఈ fseek స్టేట్‌మెంట్ కర్సర్‌ను సరిగ్గా ఇప్పుడే చదివిన రికార్డు ప్రారంభానికి వెనక్కి తెస్తుంది, తద్వారా fwrite() తో ఆ రికార్డును అక్కడికక్కడే ఇన్-ప్లేస్ అప్‌డేట్ చేయవచ్చు.
❓ ప్రశ్న 3: scanf("%d", ...) తర్వాత fgets() వాడినప్పుడు ఎందుకు స్కిప్ అవుతుంది? దానికి పరిష్కారం ఏమిటి?
సమాధానం: scanf సంఖ్యను తీసుకుని '\n' (Enter) ను బఫర్‌లో వదిలేస్తుంది. ఆ మిగిలిన న్యూలైన్‌ను fgets వెంటనే చదివి ఖాళీగా ముగిస్తుంది. పరిష్కారం: scanf తర్వాత clear_input_buffer() తో బఫర్‌ను క్లియర్ చేయాలి.
❓ ప్రశ్న 4: బైనరీ ఫైల్ నుండి రికార్డును తొలగించడానికి తాత్కాలిక ఫైల్ (Temporary File) పద్ధతిని ఎందుకు వాడతారు?
సమాధానం: ఫైల్ మధ్యలోని డేటాను నేరుగా తొలగించి ఖాళీ స్థలాన్ని కుదించడం ఫైల్ సిస్టమ్‌లో సాధ్యం కాదు. కాబట్టి డిలీట్ చేయాల్సిన రికార్డు మినహా మిగిలిన అన్నింటినీ కొత్త ఫైల్‌లోకి కాపీ చేసి, పాత ఫైల్‌ను తీసివేసి రీనేమ్ చేయడమే సురక్షితమైన మార్గం.
❓ ప్రశ్న 5: C స్టాండర్డ్ లైబ్రరీలోని qsort() ఫంక్షన్‌కు మనం ఎందుకు ఫంక్షన్ పాయింటర్‌ను కంపేరేటర్‌గా పంపుతాం?
సమాధానం: qsort() జెనెరిక్ అల్గారిథమ్ కాబట్టి దానికి మనం స్టోర్ చేసిన డేటా టైప్ (Student struct) గురించి తెలియదు. రెండు ఎలిమెంట్లలో ఏది పెద్దదో, ఏది చిన్నదో నిర్ణయించే బాధ్యతను మన కస్టమ్ కంపేరేటర్ ఫంక్షన్ నిర్వహిస్తుంది.
← Prev: 19. ఫైల్ హ్యాండ్లింగ్ 🎉 C Masterclass ఇండెక్స్ కి తిరిగి వెళ్ళండి →
OC
Written by Our Compiler Technical Editorial Team
Reviewed for accuracy & tested on GCC 13+ / Clang 17 runtime · 2026 Academic Edition