Trong bài viết này, chúng ta sẽ xây dựng một ứng dụng danh bạ đơn giản với khả năng lưu trữ dữ liệu vĩnh viễn. Đầu tiên, hãy xác định những thông tin cần thiết cho một liên hệ trong danh bạ. Thông thường, mỗi liên hệ sẽ bao gồm: họ tên, số điện thoại, giới tính, địa chỉ và năm sinh. Chúng ta sẽ biểu diễn các thông tin này thông qua một cấu trúc struct, đồng thời sử dụng các hằng số enum để định nghĩa các tham số cơ bản, giúp việc bảo trì và mở rộng code trở nên dễ dàng hơn trong tương lai.
// Định nghĩa các hằng số và tham số cấu hình
enum THAMSO
{
TEN_MAX = 20, // Độ dài tối đa của tên
SDT_MAX = 12, // Độ dài tối đa của số điện thoại
GIOI_TINH_MAX = 10, // Độ dài tối đa của giới tính
DIA_CHI_MAX = 100, // Độ dài tối đa của địa chỉ
// Các lệnh menu
LENH_THEM = 1,
LENH_XOA = 2,
LENH_TIM = 3,
LENH_SUA = 4,
LENH_HIEN = 5,
LENH_SAPXEP = 6,
LENH_THOAT = 0,
LENH_XOAALL = 7,
// Thông số bộ nhớ
KICH_THUOC_MAC_DINH = 3, // Dung lượng ban đầu
KICH_THUOC_MO_RONG = 2 // Dung lượng tăng thêm mỗi lần
};
// Cấu trúc lưu trữ thông tin cá nhân
typedef struct ca_nhan
{
char ho_ten[TEN_MAX]; // Họ và tên
int nam_sinh; // Năm sinh
char so_dien_thoai[SDT_MAX]; // Số điện thoại
char gioi_tinh[GIOI_TINH_MAX]; // Giới tính
char dia_chi[DIA_CHI_MAX]; // Địa chỉ
} ca_nhan;
Để quản lý toàn bộ danh bạ, chúng ta cần một cấu trúc có khả năng lưu trữ danh sách các liên hệ, đồng thời theo dõi số lượng hiện tại và dung lượng tối đa của bộ nhớ động.
// Cấu trúc quản lý danh bạ
typedef struct danh_ba
{
ca_nhan* du_lieu; // Con trỏ đến mảng liên hệ
size_t so_luong; // Số lượng liên hệ hiện tại
size_t dung_luong; // Dung lượng tối đa
} danh_ba;
Phần tiếp theo, chúng ta sẽ khai báo các thư viện cần thiết cho chương trình:
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<errno.h>
Bây giờ, chúng ta sẽ xây dựng hàm main với menu điều khiển. Chương trình sẽ hiển thị các tùy chọn và xử lý dựa trên lựa chọn của người dùng. Chúng ta cần các hàm để thêm, xóa, tìm kiếm, sửa, hiển thị, sắp xếp, xóa toàn bộ và lưu dữ liệu.
#include"danhba.h"
// Hàm hiển thị menu
static void hien_thi_menu()
{
printf("****************************************\n");
printf("****************************************\n");
printf("****************************************\n");
printf("*** 1.them 2.xoa ******\n");
printf("*** 3.tim 4.sua ******\n");
printf("*** 5.hien 6.sapxep ******\n");
printf("*** 0.thoat 7.xoaall ******\n");
printf("****************************************\n");
printf("****************************************\n");
printf("****************************************\n");
}
int main()
{
int lua_chon = 0; // Biến lưu lựa chọn menu
danh_ba csdl; // Tạo cấu trúc danh bạ
khoi_tao_danh_ba(&csdl); // Khởi tạo cấu trúc
do
{
hien_thi_menu();
printf("Vui long chon chuc nang:\n");
scanf("%d", &lua_chon);
switch (lua_chon)
{
case LENH_THEM:
them_lien_he(&csdl);
break;
case LENH_XOA:
xoa_lien_he(&csdl);
break;
case LENH_TIM:
tim_kiem_lien_he(&csdl);
break;
case LENH_SUA:
sua_lien_he(&csdl);
break;
case LENH_HIEN:
hien_thi_danh_ba(&csdl);
break;
case LENH_SAPXEP:
sap_xep_lien_he(&csdl);
break;
case LENH_XOAALL:
xoa_tat_ca(&csdl);
break;
case LENH_THOAT:
luu_vao_file(&csdl);
giai_phong(&csdl);
printf("Chuong trinh ket thuc\n");
break;
default:
printf("Lua chon khong hop le, vui long chon lai\n");
break;
}
} while (lua_chon);
return 0;
}
Tiếp theo, chúng ta sẽ triển khai các hàm chức năng trong file nguồn riêng biệt. Đầu tiên là hàm mở rộng dung lượng khi cần thiết.
#include"danhba.h"
// Hàm mở rộng dung lượng bộ nhớ
static void mo_rong_dung_luong(danh_ba* csdl)
{
assert(csdl);
if (csdl->so_luong == csdl->dung_luong)
{
void* vung_nho = NULL;
vung_nho = realloc(csdl->du_lieu, (csdl->dung_luong + KICH_THUOC_MO_RONG) * sizeof(ca_nhan));
if (vung_nho == NULL)
{
printf("mo_rong_dung_luong::%s\n", strerror(errno));
return;
}
csdl->dung_luong += KICH_THUOC_MO_RONG;
csdl->du_lieu = (ca_nhan*)vung_nho;
printf("Mo rong dung luong thanh cong\n");
}
}
Hàm đọc dữ liệu từ file lưu trữ trước đó:
static int doc_tu_file(danh_ba* csdl)
{
static void mo_rong_dung_luong(danh_ba* csdl);
FILE* tep = fopen("danhba.txt", "r");
if (tep == NULL)
{
return 1;
}
ca_nhan temp = { 0 };
while (fread(&temp, sizeof(ca_nhan), 1, tep) == 1)
{
mo_rong_dung_luong(csdl);
csdl->du_lieu[csdl->so_luong] = temp;
(csdl->so_luong)++;
}
fclose(tep);
tep = NULL;
return 0;
}
Hàm khởi tạo danh bạ:
void khoi_tao_danh_ba(danh_ba* csdl)
{
static int doc_tu_file(danh_ba* csdl);
assert(csdl);
csdl->so_luong = 0;
void* vung_nho = NULL;
vung_nho = calloc(KICH_THUOC_MAC_DINH, sizeof(ca_nhan));
if (csdl->du_lieu == NULL)
{
printf("khoi_tao_danh_ba:calloc:%s\n", strerror(errno));
return;
}
csdl->du_lieu = (ca_nhan*)vung_nho;
csdl->dung_luong = KICH_THUOC_MAC_DINH;
if (doc_tu_file(csdl) == 1)
{
printf("Doc du lieu cu that bai\n");
return;
}
}
Hàm thêm liên hệ mới:
void them_lien_he(danh_ba* csdl)
{
static void mo_rong_dung_luong(danh_ba* csdl);
assert(csdl);
mo_rong_dung_luong(csdl);
printf("Nhap ho ten:\n");
scanf("%s", (csdl->du_lieu + csdl->so_luong)->ho_ten);
printf("Nhap nam sinh:\n");
scanf("%d", &((csdl->du_lieu + csdl->so_luong)->nam_sinh));
printf("Nhap so dien thoai:\n");
scanf("%s", csdl->du_lieu[csdl->so_luong].so_dien_thoai);
printf("Nhap gioi tinh:\n");
scanf("%s", csdl->du_lieu[csdl->so_luong].gioi_tinh);
printf("Nhap dia chi:\n");
scanf("%s", csdl->du_lieu[csdl->so_luong].dia_chi);
csdl->so_luong++;
printf("Them thanh cong\n");
}
Hàm hiển thị toàn bộ danh bạ:
void hien_thi_danh_ba(danh_ba* csdl)
{
assert(csdl);
int i = 0;
printf("%-20s\t%-5s\t%12s\t%10s\t%30s\n", "Ho ten", "NS", "SDT", "GT", "Dia chi");
for (i = 0; i < csdl->so_luong; i++)
{
printf("%-20s\t%-5d\t%-12s\t%-10s\t%-30s\n",
csdl->du_lieu[i].ho_ten,
csdl->du_lieu[i].nam_sinh,
csdl->du_lieu[i].so_dien_thoai,
csdl->du_lieu[i].gioi_tinh,
csdl->du_lieu[i].dia_chi);
}
}
Hàm tìm kiếm theo tên:
static int tim_theo_ten(const danh_ba* csdl, const char* ten)
{
assert(csdl && ten);
int i = 0;
for (i = 0; i < csdl->so_luong; i++)
{
if (strcmp(csdl->du_lieu[i].ho_ten, ten) == 0)
{
return i;
}
}
return -1;
}
Hàm tìm kiếm liên hệ:
void tim_kiem_lien_he(danh_ba* csdl)
{
static int tim_theo_ten(const danh_ba* csdl, const char* ten);
assert(csdl);
if (csdl->so_luong == 0)
{
printf("Danh ba trong, khong tim thay\n");
return;
}
printf("Nhap ten can tim:\n");
char ten[TEN_MAX] = { 0 };
scanf("%s", ten);
int vi_tri = tim_theo_ten(csdl, ten);
if (vi_tri == -1)
{
printf("Khong tim thay\n");
return;
}
printf("%-20s\t%-5s\t%-12s\t%-10s\t%-30s\n", "Ho ten", "NS", "SDT", "GT", "Dia chi");
printf("%-20s\t%-5d\t%-12s\t%-10s\t%-30s\n",
csdl->du_lieu[vi_tri].ho_ten,
csdl->du_lieu[vi_tri].nam_sinh,
csdl->du_lieu[vi_tri].so_dien_thoai,
csdl->du_lieu[vi_tri].gioi_tinh,
csdl->du_lieu[vi_tri].dia_chi);
}
Hàm xóa liên hệ:
void xoa_lien_he(danh_ba* csdl)
{
static int tim_theo_ten(const danh_ba* csdl, const char* ten);
assert(csdl);
char ten[TEN_MAX] = { 0 };
if (csdl->so_luong == 0)
{
printf("Danh ba trong, khong xoa duoc\n");
return;
}
printf("Nhap ten can xoa:\n");
scanf("%s", ten);
int vi_tri = tim_theo_ten(csdl, ten);
if (vi_tri == -1)
{
printf("Khong tim thay\n");
return;
}
for (; vi_tri < csdl->so_luong - 1; vi_tri++)
{
csdl->du_lieu[vi_tri] = csdl->du_lieu[vi_tri + 1];
}
csdl->so_luong--;
printf("Xoa thanh cong\n");
}
Hàm sửa thông tin liên hệ:
void sua_lien_he(danh_ba* csdl)
{
static int tim_theo_ten(const danh_ba* csdl, const char* ten);
assert(csdl);
if (csdl->so_luong == 0)
{
printf("Danh ba trong, khong sua duoc\n");
return;
}
printf("Nhap ten can sua:\n");
char ten[TEN_MAX] = { 0 };
scanf("%s", ten);
int vi_tri = tim_theo_ten(csdl, ten);
if (vi_tri == -1)
{
printf("Khong tim thay\n");
return;
}
printf("Nhap ho ten moi:\n");
scanf("%s", csdl->du_lieu[vi_tri].ho_ten);
printf("Nhap nam sinh moi:\n");
scanf("%d", &csdl->du_lieu[vi_tri].nam_sinh);
printf("Nhap so dien thoai moi:\n");
scanf("%s", csdl->du_lieu[vi_tri].so_dien_thoai);
printf("Nhap gioi tinh moi:\n");
scanf("%s", csdl->du_lieu[vi_tri].gioi_tinh);
printf("Nhap dia chi moi:\n");
scanf("%s", csdl->du_lieu[vi_tri].dia_chi);
printf("Sua thanh cong\n");
}
Hàm sắp xếp liên hệ theo tên:
static int so_sanh_theo_ten(const void* a, const void* b)
{
return strcmp(((const ca_nhan*)a)->ho_ten, ((const ca_nhan*)b)->ho_ten);
}
void sap_xep_lien_he(danh_ba* csdl)
{
static int so_sanh_theo_ten(const void* a, const void* b);
assert(csdl);
if (csdl->so_luong == 0)
{
printf("Danh ba trong, khong sap xep duoc\n");
return;
}
printf("Dang sap xep theo ho ten...\n");
qsort(csdl->du_lieu, csdl->so_luong, sizeof(ca_nhan), so_sanh_theo_ten);
printf("Sap xep thanh cong\n");
hien_thi_danh_ba(csdl);
}
Các hàm giải phóng bộ nhớ, lưu và xóa dữ liệu:
void giai_phong(danh_ba* csdl)
{
assert(csdl);
free(csdl->du_lieu);
csdl->du_lieu = NULL;
}
void luu_vao_file(const danh_ba* csdl)
{
assert(csdl);
FILE* tep = fopen("danhba.txt", "wb");
if (tep == NULL)
{
printf("Luu that bai:luu_vao_file:fopen");
return;
}
fwrite(csdl->du_lieu, sizeof(ca_nhan), csdl->so_luong, tep);
fclose(tep);
tep = NULL;
}
void xoa_tat_ca(danh_ba* csdl)
{
assert(csdl);
FILE* tep = fopen("danhba.txt", "w");
if (tep == NULL)
return;
fclose(tep);
tep = NULL;
csdl->so_luong = 0;
memset(csdl->du_lieu, 0, sizeof(ca_nhan));
printf("Da xoa tat ca du lieu\n");
}
Cuối cùng, chúng ta cần khai báo các hàm trong file header để có thể sử dụng ở các file khác:
// Khởi tạo danh bạ
void khoi_tao_danh_ba(danh_ba* csdl);
// Thêm liên hệ mới
void them_lien_he(danh_ba* csdl);
// Hiển thị danh bạ
void hien_thi_danh_ba(danh_ba* csdl);
// Xóa liên hệ
void xoa_lien_he(danh_ba* csdl);
// Tìm kiếm liên hệ
void tim_kiem_lien_he(danh_ba* csdl);
// Sửa thông tin liên hệ
void sua_lien_he(danh_ba* csdl);
// Sắp xếp danh bạ
void sap_xep_lien_he(danh_ba* csdl);
// Giải phóng bộ nhớ khi thoát
void giai_phong(danh_ba* csdl);
// Lưu dữ liệu vào file
void luu_vao_file(const danh_ba* csdl);
// Xóa toàn bộ danh bạ
void xoa_tat_ca(danh_ba* csdl);
Lưu ý rằng các hàm được khai báo với từ khóa static không cần khai báo trong file header, điều này giới phạm vi sử dụng của hàm trong nội bộ file nguồn và là một thực hành tốt trong lập trình. Như vậy, chúng ta đã hoàn thành việc triển khai một ứng dụng danh bạ đơn giản với đầy đủ các chức năng cơ bản và khả năng lưu trữ dữ liệu vĩnh viễn thông qua file văn bản.