1. Đặc tả tập tin trong lập trình C

Trong lập trình C, để thực hiện đọc và ghi dữ liệu từ và vào tập tin, chúng ta sử dụng FILE, một kiểu dữ liệu được định nghĩa trong thư viện stdio.h. Dưới đây là một đặc tả cơ bản về cách làm việc với tập tin trong lập trình C:

1.1 Mở tập tin: Để mở một tập tin, bạn sử dụng hàm fopen():

FILE *fptr; // Khai báo con trỏ FILE

fptr = fopen("ten_tap_tin", "chuc_nang");

Trong đó, "ten_tap_tin" là tên của tập tin và "chuc_nang" là một chuỗi biểu thị chức năng cụ thể mà bạn muốn thực hiện trên tập tin (ví dụ: "r" cho đọc, "w" cho ghi, "a" cho ghi ở cuối tập tin, ...).

1.2. Đọc dữ liệu từ tập tin:

Để đọc dữ liệu từ tập tin, chúng ta sử dụng các hàm như fscanf() hoặc fgets().

 fscanf(fptr, "Định_dạng", &bien1, &bien2, ...);

Ví dụ:

FILE *fptr;

int num;

fptr = fopen("example.txt", "r");

fscanf(fptr, "%d", &num);

1.3 Ghi dữ liệu vào tập tin:

Để ghi dữ liệu vào tập tin, chúng ta sử dụng các hàm như fprintf() hoặc fputs().

fprintf(fptr, "Định_dạng", bien1, bien2, ...);

Ví dụ:

FILE *fptr;

int num = 42;

fptr = fopen("example.txt", "w");

fprintf(fptr, "%d", num);

1.4 Đóng tập tin:

Khi đã hoàn thành công việc với tập tin, chúng ta cần đóng nó để giải phóng tài nguyên.

fclose(fptr);

Việc đóng tập tin là quan trọng để tránh mất dữ liệu và giữ cho tập tin được lưu trữ đúng cách.

Lưu ý rằng khi làm việc với tập tin, luôn kiểm tra xem việc mở tập tin có thành công hay không bằng cách kiểm tra giá trị trả về của fopen().

 

2. Các loại tệp 

Có 2 loại tệp sau đâu:

- Tệp văn bản 

- Tệp tin nhị phân

2.1 Tệp văn bản 

- Định dạng Dữ liệu: Chứa dữ liệu ở dạng văn bản, với các ký tự được lưu trữ theo mã ASCII hoặc Unicode;  Dữ liệu trong tệp văn bản có thể đọc và hiểu được bởi con người.

- Chức Năng: Thích hợp cho việc lưu trữ thông tin văn bản, như thông điệp, cấu hình, hoặc mã nguồn chương trình.

- Mở và Chỉnh Sửa: Có thể mở và chỉnh sửa bằng các trình soạn thảo văn bản thông thường như Notepad, Visual Studio Code, hoặc gedit.

- Ví dụ: Tệp cấu hình văn bản, như các tệp .txt, .cfg, hoặc .ini.

// Ví dụ tệp văn bản

Hello, this is a text file.

Line 2: Adding more text.

2.2 Tệp tin nhị phân 

- Định dạng Dữ liệu: Chứa dữ liệu ở dạng nhị phân, không dễ đọc cho con người. Dữ liệu được lưu trữ dưới dạng chuỗi các bit và có thể bao gồm mọi loại thông tin.

- Chức Năng: Thích hợp cho việc lưu trữ dữ liệu không cấu trúc hoặc dữ liệu đồng nhất như hình ảnh, âm thanh, video.

- Mở và Chỉnh Sửa: Không thể mở và chỉnh sửa bằng các trình soạn thảo văn bản thông thường. Cần sử dụng các chương trình đặc biệt hoặc mã nguồn để đọc và xử lý dữ liệu.

- Ví dụ: Tệp hình ảnh (ví dụ: .jpg, .png), tệp âm thanh (ví dụ: .mp3, .wav).

Ví dụ tệp nhị phân (ảnh)

[01010100][10101010][11110000]...

2.3 Sự Khác Biệt:

- Dữ liệu Đọc: Trong tệp văn bản, dữ liệu có thể được đọc và hiểu bởi con người. Trong tệp nhị phân, dữ liệu thường không đọc được mà không cần một chương trình đặc biệt.

- Kích thước Tệp: Tệp văn bản thường có kích thước lớn hơn do việc lưu trữ các ký tự và định dạng văn bản. Tệp nhị phân thường nhỏ hơn vì nó lưu trữ dữ liệu theo cách gần nhất với cấu trúc nội dung.

- Đọc và Ghi:

+ Trong tệp văn bản, nội dung có thể đọc và ghi một cách dễ dàng bằng các hàm như fprintf() và fscanf().

+ Trong tệp nhị phân, cần sử dụng các hàm như fwrite() và fread() để đọc và ghi dữ liệu.

 

3. Các thao tác với file trong C

Tính năng quan trọng của ngôn ngữ lập trình C là khả năng thao tác với tệp tin, cung cấp các hàm mạnh mẽ như fopen(), fclose(), fread(), và fwrite() để thực hiện các thao tác cơ bản. Bốn thao tác chính với tệp bao gồm:

Đầu tiên, để tạo một tệp mới, ta sử dụng hàm fopen() với chế độ ghi ("w") hoặc ghi nhị phân ("wb"). Ví dụ:

FILE *filePtr;

filePtr = fopen("newfile.txt", "w");

Thứ hai, để mở một tệp đã tồn tại, ta cũng sử dụng fopen() nhưng chọn chế độ phù hợp ("r" cho đọc, "rb" cho đọc nhị phân, "w" cho ghi, "wb" cho ghi nhị phân, và như vậy). Ví dụ:

FILE *filePtr;

filePtr = fopen("existingfile.txt", "r");

Thứ ba, khi đã thực hiện các thao tác cần thiết với tệp, ta nhớ đóng tệp để giải phóng tài nguyên sử dụng hàm fclose():

fclose(filePtr);

Cuối cùng, để đọc và ghi dữ liệu từ/tới tệp, ta sử dụng các hàm như fread(), fwrite(), fscanf(), và fprintf(). Ví dụ dưới đây minh họa việc sử dụng fprintf() để ghi dữ liệu vào tệp văn bản:

FILE *filePtr;

filePtr = fopen("example.txt", "w");

if (filePtr == NULL) {

printf("Không thể mở tệp.\n");

return 1;

}

fprintf(filePtr, "Hello, World!\n");

fclose(filePtr);

Và để đọc dữ liệu từ tệp:

FILE *filePtr;

filePtr = fopen("example.txt", "r");

if (filePtr == NULL) {

printf("Không thể mở tệp.\n"); return 1;

}

char buffer[100];

fgets(buffer, sizeof(buffer), filePtr);

printf("Nội dung của tệp: %s", buffer);

fclose(filePtr);

Những thao tác cơ bản này tạo nên nền tảng cho việc làm việc linh hoạt và hiệu quả với tệp tin trong ngôn ngữ lập trình C.

 

4. Bộ tiền xử lý trong lập trình C

Bộ tiền xử lý trong ngôn ngữ lập trình C đóng vai trò quan trọng trong việc chuẩn bị mã nguồn trước khi chương trình được biên dịch. Một số chức năng cơ bản của bộ tiền xử lý là chèn nội dung từ các tệp tin, định nghĩa hằng số, thực hiện điều kiện biên dịch, và tạo ra mã nguồn linh hoạt và dễ bảo trì.

Chức năng #include cho phép chèn nội dung của một tệp tin vào mã nguồn, giúp tái sử dụng mã nguồn và tạo ra các chương trình có tổ chức. Sử dụng #define, ta có thể định nghĩa hằng số và macro, giúp tăng tính đọc hiểu của mã nguồn và dễ dàng thay đổi giá trị của chúng.

Việc kiểm soát quá trình biên dịch thông qua #ifdef, #ifndef, #else, và #endif giúp chúng ta linh hoạt khi chọn lựa mã nguồn để biên dịch dựa trên điều kiện nào đó. Chức năng #pragma cung cấp cách để truyền chỉ thị đặc biệt cho trình biên dịch, thường được sử dụng để tắt cảnh báo không mong muốn hoặc xác định các thông số cụ thể.

#undef là một cách để hủy định nghĩa một macro, giúp chúng ta linh hoạt trong việc sửa đổi định nghĩa macro trong quá trình biên dịch. Sử dụng #if, #elif, #else, và #endif giúp thực hiện kiểm tra điều kiện, giúp kiểm soát biên dịch của mã nguồn.

Cuối cùng, #error cho phép chúng ta tạo lỗi tại thời điểm biên dịch nếu một điều kiện không đúng, giúp đảm bảo tính đúng đắn của mã nguồn. Bộ tiền xử lý không chỉ là một phần của quá trình biên dịch, mà còn là công cụ mạnh mẽ để tối ưu hóa và quản lý mã nguồn trong ngôn ngữ lập trình C.

 

5. Định nghĩa của hàm fwrite()

Hàm fwrite() trong thư viện C được sử dụng để ghi dữ liệu vào một file. Dưới đây:

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

ptr: Con trỏ đến vùng nhớ chứa dữ liệu cần ghi.

size: Kích thước của mỗi phần tử dữ liệu, tính bằng byte.

nmemb: Số lượng phần tử cần ghi.

stream: Con trỏ đến FILE object, đại diện cho file mà bạn muốn ghi dữ liệu vào.

Hàm fwrite() trả về số lượng phần tử đã được ghi thành công. Nếu giá trị trả về khác với nmemb, có thể xảy ra lỗi ghi hoặc cuối file đạt được.

- Ví dụ sử dụng hàm fwrite():

#include <stdio.h>

int main() {

FILE *file; char data[] = "Hello, fwrite!";

// Mở file để ghi file = fopen("example.txt", "wb");

if (file == NULL) {

perror("Error opening file"); return 1;

}

// Ghi dữ liệu vào file

size_t elements_written = fwrite(data, sizeof(char), sizeof(data) - 1, file);

if (elements_written != sizeof(data) - 1) {

perror("Error writing to file"); fclose(file); return 1;

}

// Đóng file fclose(file);

printf("Data written to file successfully.\n");

return 0;

}

Trong ví dụ này, fwrite() được sử dụng để ghi chuỗi "Hello, fwrite!" vào một file có tên là "example.txt".

 

6. Các thao tác với file trong C của hàm fwrite

Ngôn ngữ lập trình C cung cấp một loạt các công cụ mạnh mẽ để thao tác với tệp tin, và trong số đó, hàm fwrite() là một phần quan trọng giúp ghi dữ liệu vào tệp nhị phân. Điều này làm cho việc lưu trữ và quản lý dữ liệu trở nên linh hoạt và hiệu quả.

Khi sử dụng hàm fwrite(), bước đầu tiên là mở tệp với chế độ ghi nhị phân bằng hàm fopen(). Chế độ này ("wb") đảm bảo rằng dữ liệu sẽ được ghi vào tệp mà không có bất kỳ xử lý định dạng văn bản nào, giúp bảo toàn đúng đắn các byte dữ liệu.

Sau khi tệp được mở, chúng ta tạo ra dữ liệu cần ghi, thường là thông qua cấu trúc dữ liệu hoặc mảng byte. Trong ví dụ, chúng ta sử dụng một cấu trúc ExampleStruct có các trường khác nhau như int và float.

Tiếp theo, sử dụng hàm fwrite() để ghi dữ liệu vào tệp. Tham số đầu tiên của hàm này là con trỏ đến dữ liệu cần ghi, tham số thứ hai là kích thước của mỗi mục dữ liệu, tham số thứ ba là số lượng các mục cần ghi, và tham số cuối cùng là con trỏ tới tệp.

Cuối cùng, sau khi dữ liệu đã được ghi thành công, chúng ta đóng tệp với hàm fclose() để giải phóng tài nguyên và đảm bảo tính toàn vẹn của tệp. Tổng cộng, hàm fwrite() không chỉ mang lại khả năng lưu trữ dữ liệu một cách hiệu quả mà còn cung cấp sự linh hoạt cho các lập trình viên khi làm việc với tệp nhị phân trong ngôn ngữ lập trình C.