Lập trình C: Quản lý tập tin văn bản
Có nhiều hàm khác nhau để quản lý tập tin văn bản. Chúng ta sẽ thảo luận trong các đoạn bên dưới.
Mở một tập tin văn bản
Hàm fopen() mở một stream để sử dụng và liên kết một tập tin với stream đó. Con trỏ kết hợp với tập tin được trả về từ hàm fopen(). Trong hầu hết các trường hợp, tập tin đang mở là một tập tin trên đĩa.
Nguyên mẫu của hàm fopen() là:
, trong đó filename là một con trỏ trỏ đến chuỗi ký tự chứa một tên tập tin hợp lệ và cũng có thể chứa cả phần mô tả đường dẫn. Chuỗi được trỏ đến bởi con trỏ mode xác định cách thức tập tin được mở.
Dưới đây sẽ liệt kê các cách thứ hợp lệ mà một tập tin có thể mở:
- r : Mở một tập tin văn bản để đọc
- w : Tạo một tập tin văn bản để ghi
- a : Nối vào một tập tin văn bản
- r+ :Mở một tập tin văn bản để đọc/ghi
- w+ :Tạo một tập tin văn bản để đọc/ghi
- a+f : Nối hoặc tạo một tập tin văn bản để đọc/ghi
Liệt kê trên cho thấy các tập tin có thể được mở ở nhiều chế độ khác nhau. Một con trỏ null được trả về nếu xảy ra lỗi khi hàm fopen() mở tập tin. Lưu ý rằng các chuỗi như "a+f" có thể được biễu diễn như "af+".
Giả sử nếu phải mở một tập tin xyz để ghi, câu lệnh sẽ là:
fp = fopen ("xyz", "w");
Tuy nhiên, một tập tin nói chung được mở bằng cách sử dụng một tập hợp các câu lệnh tương tự như sau:
if ((fp = fopen ("xyz", "w")) == NULL) {
printf("Cannot open file");
exit (1);
}
Macro NULL được định nghĩa trong stdio.h là ký tự '\0'. Nếu sử dụng phương pháp trên để mở một tập tin, thì hàm fopen() sẽ phát hiện ra lỗi nếu có, chẳng hạn như đĩa đang ở chế độ cấm ghi (write-protected) hay đĩa đầy, trước khi bắt đầu ghi đĩa.
Nếu một tập tin được mở để ghi, bất kỳ một tập tin nào có cùng tên và đang mở sẽ bị viết chồng lên. Vì khi một tập tin được mở ở chế độ ghi, thì một tập tin mới được tạo ra. Nếu muốn nối thêm các mẫu tin vào tập tin đã có, thì nó phải được mở với chế độ “a”. Nếu một tập tin được mở ở chế độ đọc và nó không tồn tại, hàm sẽ trả về lỗi. Nếu một tập tin được mở để đọc/ghi, nó sẽ không bị xóa nếu đã tồn tại. Tuy nhiên, nếu nó không tồn tại, thì nó sẽ được tạo ra.
Theo chuẩn ANSI, tám tập tin có thể được mở tại một thời điểm. Tuy vậy, hầu hết các trình biên dịch C và môi trường đều cho phép mở nhiều hơn tám tập tin.
Đóng một tập tin văn bản
Vì số lượng tập tin có thể mở tại một thời điểm bị giới hạn, việc đóng một tập tin khi không còn sử dụng là một điều quan trọng. Thao tác này sẽ giải phóng tài nguyên và làm giảm nguy cơ vượt quá giới hạn đã định. Đóng một stream cũng sẽ làm sạch và chép vùng đệm kết hợp của nó ra ngoài (một thao tác quan trọng để tránh mất dữ liệu) khi ghi ra đĩa. Hàm fclose() đóng một stream đã được mở bằng hàm fopen(). Nó ghi bất kỳ dữ liệu nào còn lại trong vùng đệm của đĩa vào tập tin.
Nguyên mẫu của hàm fclose() là:
, trong đó fp là một con trỏ tập tin. Hàm fclose() trả về 0 nếu đóng thành công. Bất kỳ giá trị trả về nào khác 0 đều cho thấy có lỗi xảy ra. Hàm fclose() sẽ thất bại nếu đĩa đã sớm được gỡ ra khỏi ổ đĩa hoặc đĩa bị đầy.
Một hàm khác dùng để đóng stream là hàm fcloseall(). Hàm này hữu dụng khi phải đóng cùng một lúc nhiều stream đang mở. Nó sẽ đóng tất cả các stream và trả về số stream đã đóng hoặc EOF nếu có phát hiện lỗi. Nó có thể được sử dụng theo cách như sau:
if (fcl == EOF)
printf("Error closing files");
else
printf("%d file(s) closed", fcl);
Ghi một ký tự
Streams có thể được ghi vào tập tin theo từng ký tự một hoặc theo từng chuỗi. Trước hết chúng ta hãy thảo luận về cách ghi các ký tự vào tập tin. Hàm fputc() được sử dụng để ghi các ký tự vào tập tin đã được mở trước đó bằng hàm fopen(). Nguyên mẫu của hàm này như sau:
, trong đó fp là một con trỏ tập tin trả về bởi hàm fopen() và ch là ký tự cần ghi. Mặc dù ch được khai báo là kiểu int, nhưng nó được hàm fputc() chuyển đổi thành kiểu unsigned char. Hàm fputc() ghi một ký tự vào stream đã định tại vị trí hiện hành của con trỏ định vị trí bên trong tập tin và sau đó tăng con trỏ này lên. Nếu fputc() thành công, nó trả về ký tự đã ghi, ngược lại nó trả về EOF.
Đọc một ký tự
Hàm fgetc() được dùng để đọc các ký tự từ một tập tin đã được mở ở chế độ đọc, sử dụng hàm fopen(). Nguyên mẫu của hàm là:
, trong đó fp là một con trỏ tập tin kiểu FILE trả về bởi hàm fopen(). Hàm fgetc() trả về ký tự kế tiếp của vị trí hiện hành trong stream input, và tăng con trỏ định vị trí bên trong tập tin lên.
Ký tự đọc được là một ký tự kiểu unsigned char và được chuyển thành kiểu int. Nếu đã đến cuối tập tin, fgetc() trả về EOF.
Để đọc một tập tin văn bản từ đầu cho đến cuối, câu lệnh sẽ là:
ch = fgetc(fp);
} while (ch != EOF);
Chương trình sau đây nhận các ký tự từ bàn phím và ghi chúng vào một tập tin cho đến khi người dùng nhập ký tự '@'. Sau khi người dùng nhập thông tin vào, chương trình sẽ hiển thị nội dung ra màn hình.
Ví dụ 1:
main() {
FILE *fp;
char ch= ' ';
/* Writing to file JAK */
if ((fp=fopen("jak", "w")) == NULL) {
printf("Cannot open file \n\n");
exit(1);
}
printf("Enter characters (type @ to terminate): \n");
ch = getche();
while (ch !='@') {
fputc(ch, fp) ;
ch = getche();
}
fclose(fp);
/* Reading from file JAK */
printf("\n\nDisplaying contents of file JAK\n\n");
if((fp=fopen("jak", "r")) == NULL) {
printf("Cannot open file\n\n");
exit(1);
}
do {
ch = fgetc (fp);
putchar(ch) ;
} while (ch!=EOF);
getch();
fclose(fp);
}
Một mẫu chạy cho chương trình trên là:
Enter Characters (type @ to terminate):
This is the first input to the File JAK@
Displaying Contents of File JAK
This is the first input to the File JAK
Nhập xuất chuỗi
Ngoài fgetc() và fputc(), C còn hổ trợ các hàm fputs() và fgets() để ghi vào và đọc ra các chuỗi ký tự từ tập tin trên đĩa.
Nguyên mẫu cho hai hàm này như sau:
char *fgets(char *str, int length, FILE *fp);
Hàm fputs() làm việc giống như hàm fputc(), ngoại trừ là nó viết toàn bộ chuỗi vào stream. Nó trả về EOF nếu xảy ra lỗi. Hàm fgets() đọc một chuỗi từ stream đã cho cho đến khi đọc được một ký tự sang dòng mới hoặc sau khi đã đọc được length-1 ký tự. Nếu đọc được một ký tự sang dòng mới, ký tự này được xem như là
một phần của chuỗi (không giống như hàm gets()). Chuỗi kết quả sẽ kết thúc bằng ký tự null. Hàm trả về một con trỏ trỏ đến chuỗi nếu thành công và null nếu xảy ra lỗi.