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 21.3: Các chế độ mở tập tin nhị phân.
Nếu một tập tin xyz được mở để ghi, câu lệnh sẽ là: FILE *fp;
fp = fopen ("xyz", "wb");
12.4.2 Đóng một tập tin nhị phân
Ngồi tập tin văn bản, hàm fclose() cũng có thể được dùng để đóng một tập tin nhị phân. Nguyên mẫu của fclose như sau:
int fclose(FILE *fp);
trong đó fp là một con trỏ tập tin trỏ đến một tập tin đang mở.
21.4.3 Ghi một tập tin nhị phân
Một số ứng dụng liên quan đến việc sử dụng các tập tin dữ liệu để lưu trữ các khối dữ liệu, trong đó mỗi khối bao gồm các byte liên tục. Mỗi khối nói chung sẽ biểu diễn một cấu trúc dữ liệu phức tạp hoặc một mảng.
Chẳng hạn như, một tập tin dữ liệu có thể bao gồm nhiều cấu trúc có cùng thành phần cấu tạo, hoặc nó có thể chứa nhiều mảng có cùng kiểu và kích thước. Và với những ứng dụng như vậy thường địi hỏi đọc tồn bộ khối dữ liệu từ tập tin dữ liệu hoặc ghi toàn bộ khối vào tập tin dữ liệu hơn là đọc hay ghi các thành phần độc lập (nghĩa là các thành viên của cấu trúc hay các phần tử của mảng) trong mỗi khối riêng biệt.
Hàm fwrite() được dùng để ghi dữ liệu vào tập tin dữ liệu trong những tình huống như vậy. Hàm này có thể dùng để ghi bất kỳ kiểu dữ liệu nào. Nguyên mẫu của fwrite() là:
size_t fwrite(const int *buffer, size_t num_bytes, size_t count, FILE *fp);
Kiểu dữ liệu size_t được thêm vào C chuẩn để tăng tính tương thích của chương trình với nhiều hệ thống. Nó được định nghĩa trước như là một kiểu số nguyên đủ lớn để lưu giữ kết quả của hàm
sizeof(). Đối với hầu hết các hệ thống, nó có thể được dùng như một số nguyên dương..
Buffer là một con trỏ trỏ đến thông tin sẽ được ghi vào tập tin. Số byte phải đọc hoặc ghi được
cho bởi num_bytes. Đối số count xác định có bao nhiêu mục (mỗi mục dài num_bytes) được đọc hoặc ghi. Cuối cùng, fp là một con trỏ tập tin trỏ đến một stream đã được mở trước đó. Các tập tin mở cho những thao tác này phải mở ở chế độ nhị phân.
Hàm này trả về số lượng các đối tượng đã ghi vào tập tin nếu thao tác ghi thành công. Nếu giá trị này nhỏ hơn count thì đã xảy ra lỗi. Hàm ferror() (sẽ được thảo luận trong phần tới) có thể được dùng để xác định lỗi.
12.4.4 Đọc một tập tin nhị phân
Hàm fread() có thể được dùng để đọc bất kỳ kiểu dữ liệu nào. Nguyên mẫu của hàm là: size_t fread(int *buffer, size_t num_bytes, size_t count
FILE *fp);
buffer là một con trỏ trỏ đến vùng nhớ sẽ nhận dữ liệu từ tập tin. Số byte phải đọc hoặc ghi được cho bởi num_bytes. Đối số count xác định có bao nhiêu mục (mỗi mục dài num_bytes) được
đọc hoặc ghi. Cuối cùng, fp là một con trỏ tập tin trỏ đến một stream đã được mở trước đó. Các tập tin đã mở cho những thao tác này phải mở ở chế độ nhị phân.
Hàm này trả về số lượng các đối tượng đã đọc nếu thao tác đọc thành cơng. Nó trả về 0 nếu đọc đến cuối tập tin hoặc xảy ra lỗi. Hàm feof() và hàm ferror() (sẽ được thảo luận trong phần tới) có thể được dùng để xác định nguyên nhân.
Các hàm fread() và fwrite() thường được gọi là các hàm đọc hoặc ghi không định dạng.
Miễn là tập tin được mở cho các thao tác nhị phân, hàm fread() và fwrite() có thể đọc và ghi bất kỳ kiểu thơng tin nào. Ví dụ, chương trình sau đây ghi vào và sau đó đọc ngược ra một số kiểu double, một số kiểu int và một số kiểu long từ tập tin trên đĩa. Lưu ý rằng nó sử dụng hàm
sizeof() để xác định độ dài của mỗi kiểu dữ liệu. Ví dụ 2: #include <stdio.h> main () { FILE *fp; double d = 23.31 ; int i = 13; long li = 1234567L; clrscr();
if ((fp = fopen ("jak", "wb+")) == NULL ) {
printf("Cannot open file "); exit(1);
}
fwrite (&d, sizeof(double), 1, fp); fwrite (&i, sizeof(int), 1, fp); fwrite (&li, sizeof(long), 1,fp); fclose (fp);
if ((fp = fopen ("jak", "rb+")) == NULL ) {
printf("Cannot open file"); exit(1);
}
fread (&d, sizeof(double), 1, fp); fread(&i, sizeof(int), 1, fp); fread (&li, sizeof(long), 1, fp); printf ("%f %d %ld", d, i, li); fclose (fp);
}
Như chương trình này minh họa, có thể đọc buffer và thường nó chỉ là một vùng nhớ để giữ một biến. Trong chương trình đơn giản trên, giá trị trả về của hàm fread() và fwrite() được bỏ qua. Tuy nhiên, để lập trình hiệu quả, các giá trị đó nên được kiểm tra xem đã có lỗi xảy ra không. Một trong những ứng dụng hữu dụng nhất của fread() và fwrite() liên quan đến việc đọc và ghi các kiểu dữ liệu do người dùng định nghĩa, đặc biệt là các cấu trúc. Ví dụ ta có cấu trúc sau:
struct struct_type {
float balance; char name[80]; } cust;
Câu lệnh sau đây ghi nội dung của cust vào tập tin đang được trỏ đến bởi fp. fwrite(&cust, sizeof(struct struct_type), 1, fp);
12.5 Các hàm xử lý tập tin
Các hàm xử lý tập tin khác được thảo luận trong phần này.
12.5.1 Hàm feof()
Khi một tập tin được mở để đọc ở dạng nhị phân, một số nguyên có giá trị tương đương với EOF có thể được đọc. Trong trường hợp này, quá trình đọc sẽ cho rằng đã đến cuối tập tin, mặc dù chưa đến cuối tập tin thực sự. Một hàm feof() có thể được dùng những trong trường hợp này. Nguyên mẫu của hàm là:
int feof(FILE *fp );
Nó trả về true nếu đã đến cuối tập tin, nếu khơng nó trả về false (0). Hàm này được dùng trong khi đọc dữ liệu nhị phân.
Đoạn lệnh sau đây đọc một tập tin nhị phân cho đến cuối tập tin. . . while (!feof(fp) ) ch = fgetc(fp); . . 12.5.2 Hàm rewind()
Hàm rewind() đặt lại con trỏ định vị trí bên trong tập tin về đầu tập tin. Nó lấy con trỏ tập tin làm đối số. Cú pháp của rewind() là:
rewind(fp);
Chương trình sau mở một tập tin ở chế độ đọc/ghi, sử dụng hàm fputs() với đầu vào là các chuỗi, đưa con trỏ quay về đầu tập tin và sau đó hiển thị các chuỗi giống như vậy bằng hàm fgets().
Ví dụ 3: #include <stdio.h> main() { FILE *fp; char str [80];
/* Writing to File JAK */
if ((fp = fopen("jak", "w+")) == NULL) {
printf ("Cannot open file \n\n"); exit(1);
}
clrscr (); do
{
printf ("Enter a string (CR to quit): \n"); gets (str);
if(*str != '\n')
{ strcat (str, "\n"); /* add a new line */ fputs (str, fp);
}
} while (*str != '\n'); /*Reading from File JAK */
printf ("\n\n Displaying Contents of File JAK\n\n"); rewind (fp);
while (!feof(fp)) {
fgets (str, 81, fp); printf ("\n%s", str); }
fclose(fp); }
Một mẫu chạy chương trình trên như sau:
Enter a string (CR to quit): This is input line 1
Enter a string (CR to quit) : This is input line 2
Enter a string (CR to quit): This is input line 3
Enter a string (CR to quit): Displaying Contents of File JAK This is input line 1
This is input line 2 This is input line 3
12.5.3 Hàm ferror()
Hàm ferror() xác định liệu một thao tác trên tập tin có sinh ra lỗi hay khơng. Ngun mẫu của hàm là:
int ferror(FILE * fp) ;
trong đó fp là một con trỏ tập tin hợp lệ. Nó trả về true nếu có xảy ra một lỗi trong thao tác cuối cùng trên tập tin ; ngược lại, nó trả về false. Vì mỗi thao tác thiết lập lại tình trạng lỗi, nên hàm
ferror() phải được gọi ngay sau mỗi thao tác; nếu khơng, lỗi sẽ bị mất.
Chương trình trước có thể được sửa đổi để kiểm tra và cảnh báo về bất kỳ lỗi nào trong khi ghi như sau:
. . do {
printf(“ Enter a string (CR to quit): \n"); gets(str);
if(*str != '\n')
{ strcat (str, "\n"); /* add a new line */ fputs (str, fp);
}
if(ferror(fp))
printf("\nERROR in writing\n"); } while(*str!='\n');
. .
12.5.4 Xóa tập tin
Hàm remove() xóa một tập tin đã định. Nguyên mẫu của hàm là: int remove (char *filename);
Nó trả về 0 nếu thành cơng ngược lại trả về một giá trị khác 0. Ví dụ, xét đoạn mã lệnh sau đây:
.
.
printf ("\nErase file %s (Y/N) ? ", file1); ans = getchar ();
. .
if(remove(file1)){ {
printf ("\nFile cannot be erased"); exit(1);
}
12.5.5 Làm sạch các stream
Thông thường, các tập tin xuất chuẩn được trang bị vùng đệm. Điều này có nghĩa là kết xuất cho tập tin được thu thập trong bộ nhớ và không thật sự hiển thị cho đến khi vùng đệm đầy. Nếu một chương trình bị treo hay kết thúc bất thường, một số ký tự vẫn còn nằm trong vùng đệm. Kết quả là chương trình có vẻ như kết thúc sớm hơn là nó thật sự đã làm. Hàm fflush() sẽ giải quyết vấn
đề này. Như tên gọi của nó, nó sẽ làm sạch vùng đệm và chép những gì có trong vùng đệm ra ngồi. Hành động làm sạch tùy theo kiểu tập tin. Một tập tin được mở để đọc sẽ có vùng đệm nhập trống, trong khi một tập tin được mở để ghi thì vùng đệm xuất của nó sẽ được ghi vào tập tin.
Nguyên mẫu của hàm này là:
int fflush(FILE * fp);
Hàm fflush() sẽ ghi nội dung của bất kỳ vùng đệm dữ liệu nào vào tập tin kết hợp với fp. Hàm
fflush(), khơng có đối số, sẽ làm sạch tất cả các tập tin đang mở để xuất. Nó trả về 0 nếu thành cơng, ngược lại, nó trả về EOF.
12.5.6 Các stream chuẩn
Mỗi khi một chương trình C bắt đầu thực thi dưới DOS, hệ điều hành sẽ tự động mở 5 stream đặc biệt. 5 stream này là:
Nhập chuẩn (stdin) Xuất chuẩn (stdout)