Lập trình C: Quản lý tập tin nhị phân

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

Các hàm dùng để quản 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 trong trường hợp các tập tin nhị phân.

Mở một tập tin nhị phân

Dưới đâ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:

  • rb : Mở một tập tin nhị phân để đọc
  • wb : Tạo một tập tin nhị phân để ghi
  • ab : Nối vào một tập tin nhị phân
  • r+b : Mở một tập tin nhị phân để đọc/ghi
  • w+b : Tạo một tập tin nhị phân để đọc/ghi
  • a+b : Nối vào một tập tin nhị phân để đọc/ghi

Giả sử nếu một tập tin xyz được mở để ghi, câu lệnh sẽ là:

FILE *fp;
fp = fopen ("xyz", "wb");

Đóng một tập tin nhị phân

Ngoà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ở.

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 toà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 void *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.

Đọ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(void *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ụ:

#include <stdio.h>
main () {
  FILE *fp;
  double d = 23.31 ;
  int i = 13;
  long li = 1234567L;
  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);
» Tiếp: Các hàm xử lý tập tin
« Trước: Quản lý tập tin văn bản
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 !!!