Tóm tắt bài học
Bài 12 Quản lý tập tin
12.5 Các hàm xử lý tập tin
12.5.7 Con trỏ kích hoạt hiện hành
Để lần theo vị trí nơi mà các thao tác nhập/xuất đang diễn ra, một con trỏ được duy trì trong cấu trúc FILE. Mỗi khi một ký tự được đọc ra hay ghi vào một stream, con trỏ kích hoạt hiện hành (current active pointer) (gọi là curp) được tăng lên. Hầu hết các hàm nhập xuất đều tham chiếu đến curp, và cập nhật nó sau các thủ tục nhập hoặc xuất trên stream. Vị trí hiện hành của con trỏ này có thể được tìm thấy bằng sự trợ giúp của hàm ftell(). Hàm ftell() trả về một giá trị kiểu long int biểu diễn vị trí của curp tính từ đầu tập tin trong stream đã cho. Nguyên mẫu của hàm ftell() là:
long int ftell(FILE *fp);
Câu lệnh trích từ một chương trình sẽ hiển thị vị trí của con trỏ hiện hành trong stream fp.
printf("The current location of the file pointer is : %1d ", ftell (fp));
Đặt lại vị trí hiện hành
Ngay sau khi mở stream, con trỏ kích hoạt hiện hành được đặt là 0 và trỏ đến byte đầu tiên của stream. Như đã thấy trước đây, mỗi khi có một ký tự được đọc hay ghi vào stream, con trỏ kích hoạt hiện hành sẽ tăng lên. Bên trong một chương trình, con trỏ có thể được đặt đến một vị trí bất kỳ khác với vị trí hiện hành vào bất kỳ lúc nào. Hàm rewind() đặt vị trí con trỏ này về đầu. Một hàm khác được sử dụng để đặt lại vị trí con trỏ này là fseek().
Hàm fseek() định lại vị trí của curp dời đi một số byte tính từ đầu, từ vị trí hiện hành hay từ cuối stream là tùy vào vị trí được qui định khi gọi hàm fseek(). Nguyên mẫu của hàm fseek() là:
int fseek(FILE *fp, long int offset, int origin);
trong đó offset là số byte cần di chuyển vượt qua vị trí tập tin được cho bởi tham số origin.
Tham số origin chỉ định vị trí bắt đầu tìm kiếm và phải có giá trị là 0, 1 hoặc 2, biễu diễn cho 3 hằng ký hiệu (được định nghĩa trong stdio.h) như trong bảng 21.4:
Origin Vị trí tập tin
SEEK_SET or 0 Đầu tập tin
SEEK_CUR or 1 Vị trí con trỏ của tập tin hiện hành SEEK_END or 2 Cuối tập tin
Bảng 12.4: Các hằng ký hiệu
Hàm fseek() trả về giá trị 0 nếu đã thành công và giá trị khác 0 nếu thất bại.
Đoạn lệnh sau tìm mẫu tin thứ 6 trong tập tin:
struct addr
{ char name[40];
char street[40];
char city[40];
char state[3];
char pin[7];
}FILE *fp;
. ..
fseek(fp, 5L*sizeof(struct addr), SEEK_SET);
Hàm sizeof() được dùng để tìm độ dài của mỗi mẩu tin theo đơn vị byte. Giá trị trả về được dùng để xác định số byte cần thiết để nhảy qua 5 mẩu tin đầu tiên.
12
.5.8 Hàm fprintf() và fscanf()
Ngoài các hàm nhập xuất đã được thảo luận, hệ thống nhập/xuất có vùng đệm còn bao gồm các hàm fprintf() và fscanf(). Các hàm này tương tự như hàm printf() và scanf() ngoại trừ rằng chúng thao tác trên tập tin. Nguyên mẫu của hàm fprintf() và fscanf() là:
int fprintf(FILE * fp, const char *control_string,..);
int fscanf(FILE *fp, const char *control_string,...);
trong đó fp là con trỏ tập tin trả về bởi lời gọi hàm fopen(). Hàm fprintf() và fscanf() định hướng các thao tác nhập xuất của chúng đến tập tin được trỏ bởi fp. Đoạn chương trình sau đây đọc một chuỗi và một số nguyên từ bàn phím, ghi chúng vào một tập tin trên đĩa, và sau đó đọc thông tin và hiển thị trên màn hình.
..
printf("Enter a string and a number: ");
fscanf(stdin, "%s %d", str, &no);
/* read from the keyboard */
fprintf(fp, "%s %d", str, no);
/* write to the file*/
fclose (fp);
. .
fscanf(fp, "%s %d", str, &no) /* read from file */
fprintf(stdout, "%s %d", str, no) /* print on screen */
. .
Nên nhớ rằng, mặc dù fprintf() và fscanf() thường là cách dễ nhất để ghi vào và đọc dữ liệu hỗn hợp ra các tập tin trên đĩa, nhưng chúng không phải luôn luôn là hiệu quả nhất. Nguyên nhân là mỗi lời gọi phải mất thêm một khoảng thời gian, vì dữ liệu được ghi theo dạng ASCII có định dạng (như nó sẽ xuất hiện trên màn hình) chứ không phải theo định dạng nhị phân. Vì vậy, nếu tốc độ và độ lớn của tập tin là đáng ngại, fread() và fwrite() sẽ là lựa chọn tốt hơn.
Tóm tắt
Ngôn ngữ C không chứa bất kỳ câu lệnh nhập/xuất nào tường minh. Tất cả các thao tác nhập/xuất được thực hiện bằng cách sử dụng các hàm trong thư viện chuẩn của C.
Có hai kiểu stream – stream văn bản và stream nhị phân.
Một stream văn bản là một chuỗi các ký tự.
Một stream nhị phân là một chuỗi các byte.
Một tập tin có thể là bất cứ gì từ một tập tin trên đĩa đến một thiết bị đầu cuối hay một máy in.
Một con trỏ tập tin là một con trỏ trỏ đến cấu trúc, trong đó chứa các thông tin về tập tin, bao gồm tên, vị trí hiện hành của tập tin, tập tin đang được đọc hoặc ghi, và có lỗi xuất hiện hay đã đến cuối tập tin.
Hàm fopen() mở một stream để dùng và liên kết một tập tin với stream đó.
Hàm fclose() đóng một stream đã được mở bằng hàm fopen().
Hàm fcloseall() có thể được sử dụng khi cần đóng nhiều stream đang mở cùng một lúc.
Hàm fputc() được dùng để ghi ký tự, và hàm fgetc() được dùng để đọc ký tự từ một tập tin đang mở.
Hàm fgets() và fputs() thao tác giống như hàm fgetc() và fputc(), ngoại trừ rằng chúng làm việc trên chuỗi.
Hàm feof() được dùng để chỉ ra cuối tập tin khi tập tin được mở cho các thao tác nhị phân.
Hàm rewind() đặt lại vị trí của con trỏ định vị trí về đầu tập tin.
Hàm ferror() xác định liệu một thao tác trên tập tin có sinh lỗi hay không.
Hàm remove() xóa một tập tin đã cho.
Hàm fflush() làm sạch và chép các buffer ra ngoài. Nếu một tập tin được mở để đọc, thì vùng đệm nhập của nó sẽ trống, trong khi một tập tin được mở để ghi thì vùng đệm xuất của nó được ghi vào tập tin.
Hàm fseek() có thể được sử dụng để đặt lại vị trí của con trỏ định vị bên trong tập tin.
Các hàm thư viên fread() và fwrite() được dùng để đọc và ghi toàn bộ khối dữ liệu vào tập tin.
Hệ thống nhập xuất có vùng đệm cũng bao gồm hai hàm fprintf() và fscanf(), hai hàm này tương tự như hàm printf() và scanf(), ngoại trừ chúng thao tác trên tập tin.