Lập trình C: Bài tập phần tập tin (File)

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Tóm tắt

Ngôn ngữ C không chứa bất kỳ câu lệnh nhập/xuất nào tường minh. Tất cả các thao tác nhập/xuất được thực hiện bằng cách sử dụng các hàm trong thư viện chuẩn của C.

Có hai kiểu stream – stream văn bản và stream nhị phân.

Một stream văn bản là một chuỗi các ký tự.

Một stream nhị phân là một chuỗi các byte.

Một tập tin có thể là bất cứ gì từ một tập tin trên đĩa đến một thiết bị đầu cuối hay một máy in.

Một con trỏ tập tin là một con trỏ trỏ đến cấu trúc, trong đó chứa các thông tin về tập tin, bao gồm tên, vị trí hiện hành của tập tin, tập tin đang được đọc hoặc ghi, và có lỗi xuất hiện hay đã đến cuối tập tin.

Hàm fopen() mở một stream để dùng và liên kết một tập tin với stream đó.

Hàm fclose() đóng một stream đã được mở bằng hàm fopen().

Hàm fcloseall() có thể được sử dụng khi cần đóng nhiều stream đang mở cùng một lúc.

Hàm fputc() được dùng để ghi ký tự, và hàm fgetc() được dùng để đọc ký tự từ một tập tin đang mở.

Hàm fgets() và fputs() thao tác giống như hàm fgetc() và fputc(), ngoại trừ rằng chúng làm việc trên chuỗi.

Hàm feof() được dùng để chỉ ra cuối tập tin khi tập tin được mở cho các thao tác nhị phân.

Hàm rewind() đặt lại vị trí của con trỏ định vị trí về đầu tập tin.

Hàm ferror() xác định liệu một thao tác trên tập tin có sinh lỗi hay không.

Hàm remove() xóa một tập tin đã cho.

Hàm fflush() làm sạch và chép các buffer ra ngoài. Nếu một tập tin được mở để đọc, thì vùng đệm nhập của nó sẽ trống, trong khi một tập tin được mở để ghi thì vùng đệm xuất của nó được ghi vào tập tin.

Hàm fseek() có thể được sử dụng để đặt lại vị trí của con trỏ định vị bên trong tập tin.

Các hàm thư viên fread() và fwrite() được dùng để đọc và ghi toàn bộ khối dữ liệu vào tập tin.

Hệ thống nhập xuất có vùng đệm cũng bao gồm hai hàm fprintf() và fscanf(), hai hàm này tương tự như hàm printf() và scanf(), ngoại trừ chúng thao tác trên tập tin.

Bài tập Quản Lý Tập Tin

Mục tiêu:

Kết thúc bài học này, bạn có thể:

Thực hiện các thao tác trên tập tin văn bản và tập tin nhị phân.

Mở và đóng tập tin.

Đọc từ tập tin và ghi vào tập tin.

Sử dụng con trỏ tập tin.

Các bước được cho trong bài này được giải thích cặn kẽ, dễ hiểu và tư duy cẩn thận từ đầu đến cuối.

Bài đã được viết để đáp ứng được mục tiêu học và để có thể hiểu hoàn toàn về công cụ.

Xin hãy thực hiện theo các bước một cách cẩn thận.

Phần I - Làm theo hướng dẫn

22.1 Quản lý tập tin trong C

C cung cấp một giao diện đồng nhất cho việc quản lý nhập và xuất. Các phương pháp truy cập tập tin cũng giống như các phương pháp quản lý các thiết bị khác.

Giải pháp cho tính đồng nhất này là trong C không có kiểu tập tin. C xem tất cả các tập tin là stream.

22.1.1 Đọc, ghi và truy cập dữ liệu trong tập tin

Có một số hàm xử lý tập tin trong tập tin header stdio.h. Chúng ta hãy viết một chương trình C sử dụng những hàm này. Chương trình tạo một hệ thống ngân hàng đơn giản. Các chi tiết khách hàng được nhập vào và lưu trong một tập tin gọi là customer. Chi tiết của các giao dịch như gửi tiền và rút tiền được kiểm tra hợp lệ trên tập tin customer. Các giao dịch hợp lệ được ghi nhận trong tập tin trans. Một báo cáo về các khách hàng có số vốn thấp được in ra.

Các bước được liệt kê như sau:

1. Định nghĩa một structure để lưu trữ dữ liệu về khách hàng và giao dịch. Câu lệnh sẽ là:

struct cust_st
{
nt
acc_no;
char cust_nm[30];
float bal;
};
struct tran_st
{ int
acc_no;
char trantype;
float amt;
};

2. Hiển thị một danh mục để thực hiện các thao tác khác nhau dựa trên lựa chọn của người dùng. Câu lệnh sẽ là:

while(choice != 4)
{
clrscr();
printf("\nSelect choice from menu\n\n1. Accept
customer details\n2. Record Withdrawal/Deposit
transaction\n3. Print Low Balance Report\n4.
Exit\n\nEnter choice: ");
scanf(" %d", &choice);
. .
}

3. Gọi các hàm tương ứng dựa vào lựa chọn của người dùng. Câu lệnh sẽ là:

if(choice == 1)
addcust();
else if(choice == 2)
rectran();
else if(choice == 3)
prnlowbal();

4. Trong hàm thêm chi tiết của khách hàng, định nghĩa một con trỏ tập tin để kết hợp với tập tin customer. Khai báo một biến cấu trúc để nhập dữ liệu của khách hàng. Câu lệnh sẽ là:

FILE *fp;
struct cust_st custdata;

5. Mở tập tin customer theo chế độ append để có thể thêm các mẫu tin mới. Xác nhận rằng thao tác mở tập tin đã được thực hiện. Câu lệnh sẽ là:

if((fp = fopen("customer", "a+")) == NULL)
{
printf("\nERROR opening customer file");
getch();
return;
}

6. Nhập dữ liệu khách hàng vào biến cấu trúc và ghi dữ liệu vào tập tin customer. Câu lệnh sẽ là:

fwrite(&custdata, sizeof(struct cust_st), 1, fp);

7. Đóng tập tin customer sau khi nhập dữ liệu. Câu lệnh sẽ là:

fclose(fp);

8. Trong hàm dùng để ghi các giao dịch, định nghĩa biến con trỏ để trỏ đến tập tin customer và tập tin trans. Và định nghĩa biến cấu trúc để nhập vào dữ liệu của giao dịch và đọc dữ liệu khách hàng. Câu lệnh sẽ là:

FILE *fp1, *fp2;
struct cust_st custdata;
struct tran_st trandata;

9. Mở hai tập tin theo chế độ thích hợp. Tập tin customer phải mở để đọc và cập nhật, trong khi tập tin trans phải cho phép thêm các mẫu tin mới. Câu lệnh sẽ là:

if((fp1=fopen("customer", "r+w"))==NULL)
{
printf("\nERROR opening customer file");
getch();
return;
}
if((fp2 = fopen("trans", "a+")) == NULL)
{ p
rintf("\nERROR opening transaction file");
getch();
return;
}

10. Nhập vào số tài khoản cho giao dịch và bảo đảm rằng nó tồn tại trong tập tin customer. Câu lệnh sẽ là:

while((fread(&custdata, size, 1, fp1)) == 1 && found == 'n')
{ if(
custdata.acc_no == trandata.acc_no)
{ found='y';
break;
} }

11. Để bảo đảm nhập vào một kiểu giao dịch hợp lệ, câu lệnh sẽ là:

if(trandata.trantype!='D' && trandata.trantype!='d'
&& trandata.trantype!='W' && trandata.trantype!='w')
printf("\t\tInvalid transaction type, please reenter");

12. Đối với các giao dịch rút tiền, phải bảo đảm rằng số tiền rút ra phải sẳn có trong tài khoản của khách hàng. Nếu sẳn có, cập nhật số tiền còn lại trong tài khoản. Cũng cần cập nhật số tiền trong tài khoản cho các giao dịch gửi tiền. Câu lệnh sẽ là:

if(trandata.trantype=='W' || trandata.trantype=='w')
{ if(trandata.amt>custdata.bal)
printf("\nAccount balance is %.2f. Please
reenter withdrawal amount.", custdata.bal);
else
{ custdata.bal-=trandata.amt;
.
} } else
{ custdata.bal+=trandata.amt;
. . }

13. Ghi mẫu tin chứa giao dịch mới vào tập tin trans và cập nhật mẫu tin của khách hàng trong tập tin customer. Câu lệnh sẽ là:

fwrite(&trandata, sizeof(struct tran_st), 1, fp2);
fseek(fp1, (long)(-size), 1);
fwrite(&custdata, size, 1, fp1);

Lưu ý rằng trong suốt quá trình kiểm tra số tài khoản của khách hàng, mẫu tin đọc cuối cùng là của khách hàng đang thực hiện giao dịch. Vì vậy, con trỏ tập tin của tập tin customer phải nằm ở cuối của mẫu tin cần cập nhật. Con trỏ tập tin sẽ được đặt lại vị trí về đầu của mẫu tin sử dụng hàm fseek(). Ở đây size là một biến số nguyên chứa kích cở của cấu trúc cho dữ liệu khách hàng.

14. Đóng hai tập tin sau khi đã nhập giao dịch. Câu lệnh sẽ là:

fclose(fp1);
fclose(fp2);

15. Trong hàm hiển thị các tài khoản có số vốn ít, định nghĩa con trỏ tập tin để kết hợp với tập tin customer. Khai báo một biến cấu trúc để đọc dữ liệu của khách hàng. Câu lệnh sẽ là:

FILE *fp;
struct cust_st custdata;

16. Sau khi mở tập tin ở chế độ đọc, đọc mỗi mẩu tin khách hàng và kiểm tra số vốn. Nếu nó ít hơn 250, in mẩu tin ra. Câu lệnh sẽ là:

while((fread(&custdata, sizeof(struct cust_st), 1, fp))==1)
{
if(
custdata.bal<250)
{
 . . printf("\n%d\t%s\t%.2f", custdata.acc_no,
custdata.cust_nm, custdata.bal);
}
}

17. Đóng tập tin customer. Câu lệnh sẽ là:

fclose(fp);

Chúng ta hãy nhìn vào chương trình hoàn chỉnh.

1. Mở chương trình soạn thảo mà bạn dùng để gõ chương trình C.

2. Tạo một tập tin mới.

3. Gõ vào các dòng lệnh sau đây:

#include<stdio.h>
struct cust_st
{
int acc_no;
char cust_nm[30];
float bal;
};
struct tran_st
{
int acc_no;
char trantype;
float amt;
};
void main()
{
int
choice = 1;
while(choice != 4)
{
printf("\nSelect choice from menu\n\n1. Accept customer details\n2. Record Withdrawal/Deposit transaction\n3. Print Low Balance Report\n4. Exit\n\nEnter choice: ");
scanf(" %d", &choice);
if(choice == 1)
addcust();
else if(choice == 2)
rectran();
else if(choice == 3)
prnlowbal();
}
} addcust()
{
FILE *fp;
char flag = 'y';
struct cust_st custdata;
if((fp = fopen("customer", "a+")) == NULL)
{
printf("\nERROR opening customer file");
getch();
return;
} while(flag == 'y')
{
printf("\n\nEnter Account number: ");
scanf(" %d", &custdata.acc_no);
printf("\nEnter Customer Name: ");
scanf("%s", custdata.cust_nm);
printf("\nEnter Account Balance: ");
scanf(" %f", &custdata.bal);
fwrite(&custdata, sizeof(struct cust_st), 1, fp);
printf("\n\nAdd another? (y/n): ");
scanf(" %c", &flag);
}
fclose(fp);
}
rectran()
{
FILE *fp1, *fp2;
char flag = 'y', found, val_flag;
struct cust_st custdata;
struct tran_st trandata;
int size = sizeof(struct cust_st);
if((fp1 = fopen("customer", "r+w")) == NULL)
{
printf("\nERROR opening customer file");
getch();
return;
}
if((fp2 = fopen("trans", "a+")) == NULL)
{ p
rintf("\nERROR opening transaction file");
getch();
return;
} while(flag == 'y')
{
printf("\n\nEnter Account number: ");
scanf("%d", &trandata.acc_no);
found='n';
val_flag = 'n';
rewind(fp1);
while((fread(&custdata, size, 1, fp1))==1 &&
found=='n')
{
if(custdata.acc_no == trandata.acc_no)
{
found = 'y';
break;
}
}
if(
found == 'y')
{
while(val_flag == 'n')
{
printf("\nEnter Transaction type (D/W): ");
scanf(" %c", &trandata.trantype);
if(trandata.trantype!='D' && trandata.trantype!='d' && trandata.trantype!='W' && trandata.trantype!='w')
printf("\t\tInvalid transaction type,
please reenter");
else
val_flag = 'y';
}
val_flag = 'n';
while(val_flag == 'n')
{
printf("\nEnter amount: ");
scanf(" %f", &trandata.amt);
if(trandata.trantype=='W' ||
trandata.trantype=='w')
{
if(trandata.amt > custdata.bal)
printf("\nAccount balance is %.2f. Please reenter withdrawal amount.", custdata.bal);
else
{
custdata.bal -= trandata.amt;
val_flag = 'y';
}
} else
{
custdata.bal += trandata.amt;
val_flag = 'y';
}
}
fwrite(&trandata, sizeof(struct tran_st), 1, fp2);
fseek(fp1, (long)(-size), 1);
fwrite(&custdata, size, 1, fp1);
} else
printf("\nThis account number does not
exist");
printf("\nRecord another transaction? (y/n): ");
scanf(" %c", &flag);
}
fclose(fp1);
fclose(fp2);
}
prnlowbal()
{
FILE *fp;
struct cust_st custdata;
char flag = 'n';
if((fp = fopen("customer", "r")) == NULL)
{
printf("\nERROR opening customer file");
getch();
return;
}
printf("\nReport on account balances below 250\n\n");
while((fread(&custdata, sizeof(struct cust_st), 1, fp)) == 1)
{
if(custdata.bal < 250)
{
flag = 'y';
printf("\n%d\t%s\t%.2f", custdata.acc_no, custdata.cust_nm, custdata.bal);
}
}
if(flag == 'n')
printf("\nNo account balances found below 250");
getch();
fclose(fp);
}

Để xem kết quả, thực hiện các bước sau đây:

4. Lưu tập tin với tên filesI.C.
5. Biên dịch tập tin, filesI.C.
6. Thực thi chương trình, filesI.C.
7. Trở về chương trình soạn thảo.

Kết xuất của chương trình như sau:

Select choice from menu
1. Accept customer details
2. Record Withdrawal/Deposit transaction
3. Print Low Balance Report
4. Exit

Enter choice:

Một mẫu kết xuất của hàm thêm vào chi tiết của khách hàng như sau:

Enter Account number: 123
Enter Customer Name: E.Wilson
Enter Account Balance: 2000
Add another? (y/n):

Một mẫu kết xuất của hàm thêm vào chi tiết của giao dịch như sau:
Enter Account number: 123
Enter Transaction type (D/W): W
Enter amount: 1000
Record another transaction? (y/n):

Một mẫu kết xuất của hàm hiển thị báo cáo các tài khoản có vốn thấp như sau:

Report on account balances below 250
104
Jones 200
113
Sharon 150
120
Paula 200

Phần II - Bài tập tự làm

1. Viết một chương trình C để hiển thị sự khác nhau giữa hai tập tin nhập vào như là đối số của dòng lệnh. Với mỗi sự khác nhau, hiển thị vị trí tìm thấy sự khác nhau và các ký tự của hai tập tin tại vị trí đó. Cũng cần phải bảo đảm rằng người sử dụng đã nhập vào số lượng đối số hợp lệ. Cuối cùng, hiển thị tổng số sự khác nhau đã tìm thấy.

Để làm điều này,

a. Khai báo các biến argv và argc để nhận vào đối số từ dòng lệnh.
b. Khai báo con trỏ trỏ đến hai tập tin.
c. Kiểm tra tính hợp lệ của argc để bảo đảm rằng đã nhập đúng số đối số.
d. Mở hai tập tin ở chế độ đọc.
e. Đặt một vòng lặp để đọc từng ký tự từ hai tập tin cho đến khi đến cuối cả hai tập tin.
f. Nếu các ký tự là khác nhau, hiển thị chúng cùng với vị trí của chúng. Tăng số đếm sự khác nhau lên 1.
g. Nếu đi đến cuối của một tập tin, in các ký tự còn lại trong tập tin kia như là sự khác biệt.
h. Kiểm tra số đếm sự khác nhau để hiển thị các thông báo thích hợp.
i. Đóng hai tập tin.

Bài tập tự làm

1. Viết một chương trình C để sao chép nội dung của một tập tin vào một tập tin khác loại trừ các từ aan, và the.

2. Viết một chương trình C để nhập vào hai chuỗi số. Lưu trữ mỗi chuỗi ở hai tập tin riêng biệt. Sắp xếp chuỗi trong mỗi tập tin. Trộn hai chuỗi vào một, sắp xếp và lưu lại chuỗi kết quả vào một tập tin mới. Hiển thị nội dung của tập tin mới.

Phụ lục I: Các hàm trong thư viện chuẩn

Nhập và xuất: <stdio.h>

FILE *fopen(const char *filename, const char *mode)

FILE *freopen(const char *filename, const char *mode, FILE *stream)

int fflush(FILE *stream)

int fclose(FILE *stream)

int remove(const char *filename)

int rename(const char *oldname, const char *newname)

FILE *tmpfile(void)

char *tmpnam(char s[L_tmpnam])

int setvbuf(FILE *stream, char *buf, int mode, size_t size)

void setbuf(FILE *stream, char *buf)

int fprint(FILE *stream, const char *format, ...)

int sprintf(char *s, const char *format, ...)

vprintf(const char *format, va_list arg)

vfprintf(FILE *stream, const char *format, va_list arg)

vsprintf(char *s, const char *format, va_list arg)

int fscanf(FILE *stream, const char *format, ...)

int scanf(const char *format, ...)

int sscanf(char *s, const char *format, ...)

int fgetc(FILE *stream)

char *fgets(char *s, int n, FILE *stream)

int fputc(int c, FILE *stream)

int fputs(const char *s, FILE *stream)

int getc(FILE *stream)

int getchar(void)

char *gets(char *s)

int putc(int c, FILE *stream)

int putchar(int c)

int ungetc(int c, FILE *stream)

size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream)

size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *stream)

int fseek(FILE *stream, long offset, int orogin)

long ftell(FILE *stream)

void rewind(FILE *stream)

int fgetpos(FILE *stream, fpos_t *ptr)

int fsetpos(FILE *stream, const fpos_t *ptr)

void clearerr(FILE *stream)

int feof(FILE *stream)

int ferror(FILE *stream)

void perror(const char *s)

Các hàm kiểm tra kiểu ký tự: <ctype.h>

isalnum(c)

isalpha(c)

iscntrl(c)

isdigit(c)

isgraph(c)

islower(c)

isprint(c)

ispunct(c)

isspace(c)

isupper(c)

isxdigit(c)
 

Các hàm về chuỗi: <string.h>

char *strcpy(s , ct)

char *strncpy(s , ct , n)

char *strcat(s , ct)

char *strncat(s , ct , n)

int strcmp(cs , ct)

int strncmp(cs , ct ,n)

char *strchr(cs , c)

char *strrchr(cs , c)

size_t strspn(cs , ct)

size_t strcspn(cs , ct)

char *strstr(cs , ct)

size_t strlen(cs)

char *strerror(n)

char *strtok(s , ct)

Các hàm toán học: <math.h>

sin(x)

cos(x)

tan(x)

asin(x)

acos(x)

atan(x)

atan2(x)

sinh(x)

cosh(x)

tanh(x)

exp(x)

log(x)

log10(x)

pow(x,y)

sqrt(x)

ceil(x)

floor(x)

fabs(x)

ldexp(x)

frexp(x,double *ip)

modf(x,double *ip)

fmod(x,y)

Các hàm tiện ích: <stdlib.h>

double atof(const char *s)

int atoi(const char *s

long atol(const char *s)

double strrod(const char *s, char **endp)

long strtol(const char *s, char **endp, int base)

unsigned long strtoul(const char *s, char **endp, int base)

int rand(void)

void srand(unsigned int seed)

void *calloc(size_t nobj, size_t size)

void *malloc(size_t size)

void *realloc(void *p, size_t size)

void free(void *p)

void abort(void)

void exit(int status)

int atexit(void (*fcn)(void))

int system(const char *s)

char *getenv(const char *name)

void *bsearch(const void *key, const void *base, size_t n, size_t size, int (*cmp)(const void *keyval, const void *datum))

void qsort(void *base, size_t n, size_t size, int (*cmp)(const void *, const void *))

int abs(int n)

long labs(long n)

div_t div(int num, int denom)

ldiv_t ldiv(long num , long denom)

» Tiếp: Quiz
« Trước: Bài tập phần cấu trúc (struct)
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!