首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言實戰:檔案式姓名號碼查詢系統——結構體、二進制檔案操作與選單驅動架構

     編輯:關於C語言

專案概覽

本系統實現一個簡易的**姓名-號碼查詢管理系統**,採用 C 語言核心特性:

  • 結構體 封裝資料實體(姓名 + 號碼)
  • 二進制檔案 持久化儲存(`fwrite`/`fread` 直接讀寫記憶體映像)
  • 選單驅動迴圈 交互式使用者介面
  • 模組化函數設計:`menu()`、`save()`、`search()`、`main()` 職責分離

資料結構定義

struct student {
    char name[7];   // 姓名,最多 6 字元 + '\0'
    int number;     // 號碼(整數)
} student, student1;  // 宣告兩個全域變數:寫入用、讀取用

設計考量:

  • 固定長度 `char name[7]` 而非動態字串,配合二進制檔案定長記錄,讓 `fread`/`fwrite` 能按 `sizeof(struct student)` 精確跳轉。
  • 全域變數簡化參數傳遞,但降低了執行緒安全性與模組獨立性(教學用途可接受)。

核心功能模組解析

1. 主選單與輸入迴圈

void menu() {
    printf("***********************\n");
    printf("1 input record\n");
    printf("2 search record\n");
    printf("0 quit system\n");
}

int main() {
    int select;
    menu();
    printf("\nPlease enter your select\n");
    scanf("%d", &select);
    while (select) {  // 非 0 繼續
        switch (select) {
            case 1: save(); break;
            case 2: search(); break;
            case 0: exit(1);  // 實際不會走到這,while 條件已攔截
        }
        // ⚠️ 缺陷:執行完 save()/search() 後不會重新顯示選單與讀取 select
        // 會陷入無限迴圈或直接退出
    }
}

關鍵 Bug 修正: `save()` 和 `search()` 內部呼叫了 `exit(0)`/`exit(1)`,導致程式直接結束,無法返回主選單。正確架構應移除函數內的 `exit()`,改回主迴圈重新讀取選項:

int main() {
    int select;
    while (1) {
        menu();
        printf("\nPlease enter your select: ");
        scanf("%d", &select);
        switch (select) {
            case 1: save(); break;
            case 2: search(); break;
            case 0: printf("Goodbye!\n"); return 0;
            default: printf("Invalid option!\n");
        }
    }
}

2. 記錄輸入與寫入(save)

void save() {
    FILE *fp = fopen("filename", "a+b");  // 二進制附加模式
    if (!fp) { perror("open file error"); return; }
    
    while (1) {
        printf("Enter name and number: ");
        scanf("%6s %d", student.name, &student.number);  // %6s 防止緩衝區溢位
        fwrite(&student, sizeof(struct student), 1, fp);
        
        printf("Continue? (y/n): ");
        char ch; scanf(" %c", &ch);  // 空白吃掉殘留換行
        if (ch == 'n' || ch == 'N') break;
    }
    fclose(fp);
}

改進點:

  • 使用 `"a+b"` 二進制模式跨平台相容(Windows 下文字模式會轉換 `\n`/`\r\n` 破壞二進制結構)。
  • `%6s` 限制讀取寬度,防止 `name[7]` 溢位。
  • `scanf(" %c", &ch)` 前置空白自動跳過緩衝區殘留換行符。
  • 移除 `system("cls")` 與 `exit()`,提升可移植性與流程控制。

3. 記錄查詢(search)

void search() {
    FILE *fp = fopen("filename", "rb");
    if (!fp) { perror("open file error"); return; }
    
    char target[7];
    printf("Enter name to search: ");
    scanf("%6s", target);
    
    int found = 0;
    while (fread(&student1, sizeof(struct student), 1, fp) == 1) {
        if (strcmp(student1.name, target) == 0) {
            printf("Found: %s %d\n", student1.name, student1.number);
            found = 1;
            break;
        }
    }
    if (!found) printf("Record not found.\n");
    fclose(fp);
}

原代碼嚴重邏輯錯誤修正:

// 原代碼錯誤:
while (fread(...) == 0) {  // 條件反了!fread 成功返回 1,失敗/結束返回 0
    if (strcmp(...) == 0) ...
}
// 導致:檔案為空或讀完才進入迴圈,且漏讀第一筆記錄

正確邏輯:`while (fread(...) == 1)` 逐筆讀取並比對。

二進制檔案儲存格式解析

檔案 `filename` 內容為連續的定長記錄:

[Record 1: 7 bytes name][4 bytes number][Record 2: 7 bytes name][4 bytes number]...
實際記憶體佈局(小端序):
'J' 'o' 'h' 'n' '\0' '\0' '\0' | 0x39 0x05 0x00 0x00  (號碼 1337)
'A' 'l' 'i' 'c' 'e' '\0' '\0' | 0x78 0x00 0x00 0x00  (號碼 120)

優點: 讀寫極快(記憶體直接映射)、程式碼簡潔。
缺點: 不可跨平台(結構體對齊、端序、int 大小可能不同)、不支援可變長度字串、難以人工閱讀/編輯。

擴展建議:從教學玩具到實用工具

方向技術點實現提示
資料結構升級動態陣列/鏈表記憶體中維護鏈表,退出時序列化存檔
檔案格式現代化JSON / SQLite引入 cJSON 庫或 SQLite3,支援查詢、修改、刪除
查詢效能優化索引 / 雜湊表姓名建立雜湊索引,O(1) 定位記錄位移
介面增強ncurses / GUI文字介面選單、鍵盤導航、分頁顯示
健壯性加強輸入驗證、錯誤恢復檔案鎖定、異常中斷恢復、重複姓名處理

總結: 這個小系統雖簡陋(含多處 Bug),卻完整串起了 **C 語言檔案操作、結構體、選單迴圈、字串處理** 等核心知識點。對初學者極具價值:修復 Bug 的過程正是深入理解 `fread` 返回值、`scanf` 緩衝區、二進制 vs 文字模式、流程控制邏輯的最佳實踐。建議以此為基礎,逐步重構為健壯、可擴展的聯繫人管理系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved