C là một ngơn ngữ lập trình có cấu trúc, tuy vậy nó vẫn chứa một số câu lệnh làm phá vớ cấu trúc của chương trình:
Bảng 12.2: Các chế độ mở tập tin văn bản.
Bảng 21.2 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+”.
Nếu phải mở một tập tin xyz để ghi, câu lệnh sẽ là: FILE *fp;
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:
FILE *fp;
if ((fp = fopen ("xyz", "w")) == NULL) {
printf("Cannot open file"); exit (1);
}
Macro NULL được định nghĩa trong stdio.h là ‘\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.
12.3.2 Đó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 ngồ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à:
int fclose(FILE *fp);
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:
fcl = fcloseall(); if (fcl == EOF)
printf("Error closing files"); else
printf("%d file(s) closed", fcl);
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:
int fputc(int ch, FILE *fp);
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.
12.3.4 Đọ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à:
int fgetc (FILE *fp);
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à: do
{
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: #include <stdio.h> main() { FILE *fp; char ch= ' ';
/* Writing to file JAK */
if ((fp=fopen("jak", "w"))==NULL) {
printf("Cannot open file \n\n"); exit(1);
}
clrscr();
printf("Enter characters (type @ to terminate): \n"); ch = getche();
{
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
12.3.5 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:
int fputs(const char *str, FILE *fp);
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 tồ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.
Các hàm dùng để xử lý các tập tin nhị phân cũng giống như các hàm sử dụng để quản lý tập tin văn bản. Tuy nhiên, chế độ mở tập tin của hàm fopen() thì khác đi trong trường hợp các tập tin nhị phân.
12.4.1 Mở một tập tin nhị phân
Bảng sau đây liệt kê các chế độ khác nhau của hàm fopen() trong trường hợp mở tập tin nhị phân.