系統架構
- 資料結構:單向鏈結串列,節點含商品屬性(名稱、編號、產地、進貨日期、供應商、單價、庫存)
- 持久化:文字檔
goods.txt,啟動時載入、新增時追加
- 介面:主選單(錄入/刪除/查詢/修改/插入/退出)
核心資料結構
typedef struct Goods {
char name[50];
char number[20];
char origin[50];
char purchase_date[20];
char supplier[50];
int price;
int stock;
struct Goods *next;
int index; // 原始節點序號
} Goods;
關鍵函數實現
檔案讀取初始化
Goods* load_from_file() {
FILE *fp = fopen("goods.txt", "r");
if (!fp) {
fp = fopen("goods.txt", "w"); // 不存在則建立
if (!fp) return NULL;
}
fclose(fp);
fp = fopen("goods.txt", "r");
Goods *head = NULL, *tail = NULL;
char line[512];
while (fgets(line, sizeof(line), fp)) {
Goods *node = malloc(sizeof(Goods));
sscanf(line, "%d %49s %19s %49s %19s %49s %d %d",
&node->index, node->name, node->number, node->origin,
node->purchase_date, node->supplier, &node->price, &node->stock);
node->next = NULL;
if (!head) head = tail = node;
else { tail->next = node; tail = node; }
}
fclose(fp);
return head;
}
新增商品並寫入檔案
void save_to_file(Goods *node) {
FILE *fp = fopen("goods.txt", "a");
if (!fp) return;
fprintf(fp, "%d %s %s %s %s %s %d %d\n",
node->index, node->name, node->number, node->origin,
node->purchase_date, node->supplier, node->price, node->stock);
fclose(fp);
}
鏈結串列刪除節點
Goods* delete_node(Goods *head, Goods *target) {
if (!head || !target) return head;
if (head == target) {
Goods *tmp = head->next;
free(head);
return tmp;
}
Goods *prev = head;
while (prev->next && prev->next != target) prev = prev->next;
if (prev->next) {
prev->next = target->next;
free(target);
}
return head;
}
實戰踩坑記錄
- 檔案不存在導致閃退:
fopen(..., "r") 要求檔案必須存在 → 先用 "w" 建立再用 "r" 讀取
- 控制台一閃而過:讀取失敗
exit(1) 直接結束 → 加入 getchar() 暫停觀察錯誤訊息
- 隱藏副檔名導致雙重副檔名:Windows 預設隱藏副檔名,實際檔名為
goods.txt.txt → 程式檢查檔案存在性時用 fopen 實測而非檔名字串
scanf 留下換行符:混用 scanf 與 fgets/getline 時需清理緩衝區
編譯環境修正
- 原碼混用 C/C++(
iostream、string、new/delete) → 純 C 版本改用 stdio.h、stdlib.h、string.h、malloc/free
- 結構體中
string 改為定長字元陣列
_getch() 為 MSVC 專用,跨平台可用 getchar() 或 termios 非緩衝讀取
擴展建議
- 加入排序功能(按單價/庫存)
- 實作二分搜尋(需轉為陣列或平衡樹)
- 引入 SQLite 替代文字檔,支援並發與複雜查詢
- 介面改用 ncurses 實現文字模式視窗化