Giáo án Tin học Lớp 11 - Bài 21: Quản lý tập tin
Mục tiêu:
Kết thúc bài học này, bạn có thể:
Giải thích khái niệm luồng (streams) và tập tin (files)
Thảo luận các luồng văn bản và các luồng nhị phân
Giải thích các hàm xử lý tập tin
Giải thích con trỏ tập tin
Thảo luận con trỏ kích hoạt hiện hành
Giải thích các đối số từ dòng nhắc lệnh (command-line).
Giới thiệu
Hầu hết các chương trình đều yêu cầu đọc và ghi dữ liệu vào các hệ thống lưu trữ trên đĩa. Các chương trình xử lý văn bản cần lưu các tập tin văn bản, chương trình xử lý bảng tính cần lưu nội dung của các ô, chương trình cơ sỡ dữ liệu cần lưu các mẫu tin. Bài này sẽ khám phá các tiện ích trong C dành cho các thao tác nhập/xuất (I/O) đĩa hệ thống.
Ngôn ngữ C không chứa bất kỳ câu lệnh nhập/xuất nào một cách tường minh. Tất cả các thao tác nhập/xuất đều thực hiện thông qua các hàm thư viện chuẩn của C. Tiếp cận này làm cho hệ thống quản lý tập tin của C rất mạnh và uyển chuyển. Nhập/xuất trong C là tuyệt vời vì dữ liệu có thể truyền ở dạng nhị phân hay ở dạng văn bản mà con người có thể đọc được. Điều này làm cho việc tạo tập tin để đáp ứng mọi nhu cầu một cách dễ dàng.
Việc hiểu rõ sự khác biệt giữa stream và tập tin là rất quan trọng. Hệ thống nhập/xuất của C cung cấp cho người dùng một giao diện độc lập với thiết bị thật sự đang truy cập. Giao diện này không phải là một tập tin thật sự mà là một sự biễu diễn trừu tượng của thiết bị. Giao diện trừu tượng này được gọi là một stream và thiết bị thật sự được gọi là tập tin.
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); } 21.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 ngoà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. 21.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) Lỗi chuẩn (stderr) Máy in chuẩn (stdprn) Thiết bị hỗ trợ chuẩn (stdaux) Trong đó, stdin, stdout và stderr được gán mặc định cho các thiết bị nhập/xuất chuẩn của hệ thống trong khi stdprn được gán cho cổng in song song đầu tiên và stdaux được gán cho cổng nối tiếp đầu tiên. Chúng được định nghĩa như là các con trỏ cố định kiểu FILE, vì vậy chúng có thể được sử dụng ở bất kỳ nơi nào mà việc sử dụng con trỏ FILE là hợp lệ. Chúng cũng có thể được chuyển một cách hiệu quả cho các stream hay thiết bị khác mỗi khi cần định hướng lại. Chương trình sau đây in nội dung của tập tin vào máy in. Ví dụ 4: #include main() { FILE *in; char buff[81], fname[13]; clrscr(); printf("Enter the Source File Name:"); gets(fname); if((in=fopen(fname, "r"))==NULL) { fputs("\nFile not found", stderr); /* display error message on standard error rather than standard output */ exit(1); } while(!feof(in)) { if(fgets(buff, 81, in)) { fputs(buff, stdprn); /* Send line to printer */ } } fclose(in); } Lưu ý cách sử dụng của stream stderr với hàm fputs() trong chương trình trên. Nó được sử dụng thay cho hàm printf vì kết xuất của hàm printf là ở stdout, nơi mà có thể định hướng lại. Nếu kết xuất của một chương trình được định hướng lại và một lỗi xảy ra trong quá trình thực thi, thì tất cả các thông báo lỗi đưa ra cho stream stdout cũng phải được định hướng lại. Để tránh điều này, stream stderr được dùng để hiển thị thông báo lỗi lên màn hình vì kết xuất của stderr cũng là thiết bị xuất chuẩn, nhưng stream stderr không thể định hướng lại. Nó luôn luôn hiển thị thông báo lên màn hình. 21.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 21.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. 21.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. Kiểm tra tiến độ học tập Có hai kiểu stream là stream __________ và stream _________. Các tập tin đang mở được đóng lại khi chương trình bị treo hay kết thúc bất thường. (Đúng /Sai) Hàm _________ mở một stream để dùng và liên kết một tập tin với stream đó. Hàm được dùng để ghi ký tự vào tập tin là ________. Hàm fgets() xem ký tự sang dòng mới như là một phần của chuỗi. (Đúng / Sai) Hàm ________ đặt lại vị trí của con trỏ định vị bên trong tập tin về đầu tập tin. Mỗi khi một ký tự được đọc hay ghi từ một stream, ___________ được tăng lên. Các tập tin mà trên đó hàm fread() và fwrite() thao tác thì phải được mở ở chế độ ________. Vị trí hiện hành của con trỏ kích hoạt hiện hành có thể được tìm thấy bằng sự trợ giúp của hàm ________. Bài tập tự làm Viết một chương trình để nhập dữ liệu vào một tập tin và in nó theo thứ tự ngược lại. Viết một chương trình để truyền dữ liệu từ một tập tin này sang một tập tin khác, loại bỏ tất cả các nguyên âm (a, e, i, o, u). Loại bỏ các nguyên âm ở dạng chữ hoa lẫn chữ thường. Hiển thị nội dung của tập tin mới.
File đính kèm:
- Session 21- Concept.doc