7.5. Kiểu tệp (FILE) 7.5.1. Khái niệm
Nhập và xuất dữ liệu là hai công việc rất phổ biến khi thực hiện một chương trình. Cho đến nay, ta mới chỉ nhập dữ liệu từ bàn phím và xuất dữ liệu ra màn hình. Các dữ liệu này được tổ chức trong bộ nhớ của máy, chúng tồn tại khi chương trình đang chạy và bị xóa khi chương trình kết thúc. Muốn lưu trữ các dữ liệu lâu dài để sử dụng nhiều lần thì phải ghi chúng lên đĩa thành các tệp.
Tệp (file) trong Pascal là một kiểu dữ liệu có cấu trúc. Mỗi tệp là một tập hợp các phần tử có cùng chung một kiểu dữ liệu được nhóm lại thành một dãy và được ghi trên đĩa dưới một cái tên chung. Khái niệm tệp và mảng có những điểm rất gần nhau. Song tệp khác mảng ở những điểm sau đây:
Mảng được tổ chức trong bộ nhớ còn tệp chủ yếu được tổ chức trên đĩa.
Số phần tử của mảng được xác định ngay khi khai báo, còn số phần tử của tệp thì không. Các tệp được kết thúc bằng một dấu hiệu đặc biệt gọi là EOF ( End Of File).
Các phần tử của mảng được truy xuất thông qua chỉ số. Các phần tử của tệp được truy xuất nhờ một biến trung gian chỉ điểm vào vị trí của chúng trên đĩa, gọi là con trỏ tệp. Tại mỗi thời điểm, con trỏ sẽ chỉ vào một vị trí nào đó trong tệp, gọi là vị trí hiện thời.
Cách khai báo kiểu tệp như sau:
Type Tên_kiểu_Tệp = File of Kiểu_phần_tử ;
Ví dụ 7.57:
Type Ksvien = Record
Ten: String[20];
Namsinh : Integer;
DTB : Real;
end;
KieuT1 = File of Integer;
KieuT2 = File of String[20];
KieuT3 = File of Ksvien ;
Theo khai báo trên thì KieuT1 là tệp có các phần tử kiểu nguyên (Integer), KieuT2 là tệp có các phần tử là các chuỗi ký tự ( String[20] ), còn KieuT3 là tệp có các phần tử là các bản ghi kiểu Ksvien.
Khi đã có kiểu tệp, ta có thể khai báo các biến tệp :
Var F1 : KieuT1;
F2 : KieuT2;
F3 : KieuT3;
F1, F2, F3 là các biến kiểu tệp, một loại biến đặc biệt, không dùng để gán giá trị như các biến nguyên, thực hay chuỗi. Mỗi biến này đại diện cho một tệp mà thông qua các biến đó ta có thể thực hiện các thao tác trên tệp như : tạo tệp, mở, đóng, xóa tệp, ghi dữ liệu vào tệp và đọc dữ liệu từ tệp, ...
Ngoài cách khai báo các biến F1, F2, F3 thông qua việc định nghĩa các kiểu dữ liệu mới như trên, Pascal còn cho phép khai báo trực tiếp các biến tệp như sau:
Var TênbiếnTệp : File of Kiểuphầntử ;
Ví dụ 7.58: có thể khai báo ba biến F1, F2, F3 nói trên theo cách sau :
Type Ksvien = Record
Ten: String[20];
Namsinh : Integer;
DTB : Real;
end;
Var F1 : File of Integer;
F2 : File of String[20];
F3 : File of Ksvien ;
7.5.2. Cấu trúc và phân loại tệp
Các phần tử của tệp không có tên nên truy cập không thể tùy tiện được. Các phần tử của tệp được sắp xếp thành một dãy và mỗi thời điểm chương trình chỉ có thể truy cập tệp thông qua một biến đệm. Biến đệm dùng để đánh dấu vị trí truy cập tệp hay còn gọi là cửa sổ tệp. Mỗi tệp đều có một ký hiệu kết thúc gọi là EOF(F) - End of File F. Việc phân loại tệp dựa trên việc bố trí các phần tử của tệp và cách truy cập tệp: truy cập tệp tuần tự và truy cập tệp trực tiếp.
7.5.3. Tệp định kiểu
Như đã nêu ở phần trên, tệp là cấu trúc để lưu trữ nhiều dữ liệu. Nếu tệp lưu các phần tử dữ liệu cùng kiểu thì ta có tệp định kiểu.
- Tệp định kiểu là tập hợp các phần tử dữ liệu cùng kiểu, xếp liền nhau;
- Số phần tử hay kích thước của tệp có thể thay đổi trong thời gian thực hiện chương trình ;
- Kích thước tệp không được xác định khi khai báo.
Cú pháp: Type tên kiểu tệp = File of kiểu phần tử ;
Trong đó kiểu phần tử của tệp gọi là kiểu cơ sở, có thể là bất kì kiểu gì, trừ kiểu tệp.
Ví dụ 7.59:
TYPE TepSoThuc = FILE OF Real ;
TepSoNguyen = FILE OF Integer ;
TepHoSo = FILE OF LyLich ;
VAR F1: TepSoThuc ;
F2: TepSoNguyen ;
F3: TepHoSo ;
Cũng có thể khai báo tắt, kết hợp khai báo biến với mô tả kiểu tệp:
F1: FILE OF Real ;
F2: FILE OF Integer;
F3: FILE OF LyLich ;
Biến kiểu tệp là biến đại diện cho tệp. Mọi việc truy cập tệp đều phải thông qua biến tệp.
7.5.4. Tệp truy cập tuần tự Mô tả cấu trúc
Theo định nghĩa, tệp là tập hợp các phần tử dữ liệu cùng kiểu, xếp liền nhau, do đó có thể coi tệp như một mảng với kích thước co giãn được.
Cấu trúc của mảng có khuôn mẫu cố định nên có thể truy cập trực tiếp vào các phần tử qua chỉ số mảng. Trái lại, các phần tử làm nên tệp không được đánh chỉ số, vì có thể bị di chuyển do các thao tác thêm bớt vào nội dung.
Tại một thời điểm chỉ có thể truy cập vào một phần tử thông qua giá trị của một biến đệm.
-
Có thể minh hoạ biến đệm như một cửa sổ di động dọc theo tệp để nhìn vào các phần tử tệp. Tại một thời điểm của sổ này nằm ở một vị trí xác định.
Cửa sổ sẽ tự động dịch đến phần tử kế tiếp sau mỗi lần truy cập.
Có dấu hiệu đặc biệt EOF tại vị trí kết thúc tệp và hàm chuẩn Eof (F) cho biết cửa sổ tệp của tệp F đã ở phần tử cuối tệp hay chưa. Eof (F) = True nếu đã ở vị trí cuối tệp, Eof (F) = False nếu trái lại.
Tệp được tổ chức như trên gọi là tệp truy cập tuần tự - Sequential access.
Với tệp truy cập tuần tự, muốn đọc một phần tử nào đó phải đi qua các phần tử đứng trước. Muốn thêm một phần tử phải đưa cửa sổ về cuối tệp.
Hình ảnh của tệp truy cập tuần tự là băng từ.
Tóm lại, tệp truy cập tuần tự có cấu trúc đơn giản và dễ xây dựng nhưng kém linh hoạt.
Pascal chuẩn chỉ định nghĩa tệp truy cập tuần tự. TurboPascal có cung cấp kiểu tệp truy cập trực tiếp, cho phép đặt của sổ tệp vào một vị trí bất kì trong tệp và truy cập các phần tử tệp tương tự như truy cập mảng.
7.5.5. Mở tệp mới để ghi dữ liệu
Chương trình chỉ có thể ghi dữ liệu vào tệp sau khi ta đã làm thủ tục mở tệp. Việc mở tệp được tiến hành nhờ hai thủ tục sau:
ASSIGN( biếntệp, têntệp);
REWRITE( biếntệp);
Ví dụ 7.60: Gán tên tệp cho biến tệp, ở đây tên tệp là một biểu thức kiểu chuỗi tên thực sự của tệp.
Assign(F1,‘DLIEU.DAT’);
khởi tạo tệp DLIEU.DAT nhờ thủ tục
Rewrite(F1) ;
Assign(F3,‘QLSV.DAT’);
Rewrite(F3); {khởi tạo tệp QLSV.DAT}
Sau hai lệnh này máy khởi tạo tệp mới, nếu tệp đã có trên đĩa thì nó xóa đi và tạo mới, biến F1 đồng nhất với tệp DLIEU.DAT, mọi thao tác trên biến F1 chính là thao tác trên tệp DLIEU.DAT. Tương tự, biến F3 đồng nhất với tệp QLSV.DAT
Sau khi mở tệp xong, tệp sẽ rỗng vì chưa có phần tử nào, cửa sổ của tệp sẽ không có giá trị xác định vì nó trỏ vào cuối tệp.
*Chú ý:
- Nếu muốn tạo ra tệp trong một thư mục khác thì tên tệp phải bao gồm cả đường dẫn.
- Nếu trên đĩa đã có sẵn một tệp trùng tên thì nội dung của tệp này sẽ bị xoá. Tệp trở thành rỗng.
* Ghi dữ liệu vào tệp với thủ tục WRITE
Thủ tục WRITE sẽ ghi các giá trị mới vào tệp
WRITE( biếntệp, b1, b2, ..., bN);
Tuần tự ghi vào tệp các giá trị của các biến b1, b2, ..., bN. Các biến b1, ..., bN phải cùng kiểu dữ liệu với các phần tử của tệp.
Ví dụ 7.61:
- Cho i, j, k là các biến kiểu Integer và i=10, j=20, k=100, khi đó lệnh :
Write(F1,i,j,k);
sẽ ghi lần lượt các giá trị 10, 20, 100 vào tệp DLIEU.DAT
- Cho khai báo:
Var X : Ksvien;
Các lệnh sau gán giá trị cho X và ghi X vào tệp QLSV.DAT:
X.Ten:=’Nguyen Thanh Lam’;
X.Namsinh :=2002;
X.DTB :=6.5;
Write(F3, X);
Sau khi ghi X vào tệp QLSV.DAT, con trỏ tệp tự động dời đến vị trí của phần tử tiếp theo.
* Đóng tệp
Bước cuối cùng của việc ghi dữ liệu vào tệp là đóng tệp lại bằng thủ tục CLOSE để đảm bảo thông tin trên tệp là đầy đủ và tin cậy.
CLOSE( biếntệp);
Ví dụ 7.62: Chương trình sau ghi 10 số nguyên từ 1 đến 10 vào tệp SoNguyen.DAT
Program Ghi_Tep;
Var I : Integer;
f : File Of Integer;
Begin
Writeln('THU TUC WRITE, GHI DU LIEU VAO TEP TREN DIA');
Writeln('-------------------------------------------');
Writeln;
Assign(f,'SoNguyen.DAT');
Rewrite(f);
For i := 1 To 10 Do
Write(f,i);
Writeln;
Writeln('Da ghi vao tep SONGUYEN');
Writeln(' Bam ...');
Readln;
Close(f);
End.
7.5.6. Mở tệp đã tồn tại để đọc dữ liệu
Đối với các tệp tuần tự, ta không thể vừa ghi vừa đọc được cùng một lúc. Sau khi ghi dữ liệu vào tệp và đóng lại, ta có thể đọc lại các giá trị đã ghi trong tệp.
Một chương trình muốn sử dụng các dữ liệu đã chứa trong một tệp, đầu tiên phải mở nó ra để đọc.
Ta có hai thủ tục sau:
ASSIGN( biếntệp, têntệp);
RESET( biếntệp);
Sau lệnh RESET con trỏ tệp trỏ vào phần tử đầu tiên (có số thứ tự là 0) của tệp.
Ví dụ 7.63: Assign(F3, ‘QLSV.DAT’);
Reset ( F3); mở tệp QLSV.DAT
*đọc dữ liệu từ một tệp đã có
Để đọc dữ liệu từ tệp ta dùng thủ tục READ có cú pháp như sau:
READ( biếntệp, b1, b2, ..., bN);
Đọc tuần tự các phần tử của tệp từ vị trí hiện thời của con trỏ tệp và gán cho các biến b1, b2, ..., bN. Kiểu dữ liệu của các biến b1, b2, ..., bN phải cùng kiểu với các phần tử của tệp. Mỗi khi đọc xong một phần tử, con trỏ tệp tự động dời đến phần tử tiếp theo.
Ví dụ 7.64:
Lệnh Read(F1, i, j); đọc hai số nguyên trong tệp DLIEU.DAT (kể từ vị trí hiện thời) và gán cho các biến nguyên i, j .
Lệnh Read(F3, X); đọc bản ghi hiện thời của tệp QLSV.DAT và gán cho biến bản ghi X.
Chương trình sau sẽ đọc các phần tử của một tệp có tên được nhập từ bàn phím (kiểm tra sự tồn tại của tệp).
Program Doc_Tep;
Label tt;
Var
I,k : Integer;
Ten : String;
f : File Of Integer;
Begin
Writeln('THU TUC READ, DOC DU LIEU TU TEP TREN DIA');
Writeln('------------------------------------------');
tt:Writeln;
k := 0;
Write('-Cho biet ten tap tin: ');
Readln(Ten);
Assign(f,Ten);
{$I-}
Reset(f);
If IOResult <> 0 Then
{ham IOResult =0 tuc tep f co ton tai}
Begin
Writeln;
Writeln(' Khong co tep nay');
Write(' Bam de tim lai: ');
Readln;
Goto tt;
End;
While Not EOF(f) Do
Begin
Read(f,i);
Writeln(i);
k := k+1;
End;
Writeln;
Writeln('Tep: ',ten,' co: ',k:3,' phan tu');
Write(' Bam ...');
Readln;
Close(f);
End.
Ví dụ 7.65:
Nhập một danh sách sinh viên gồm Họ tên, điểm Toán, điểm Lý, tính điểm trung bình rồi lưu vào tệp HOSO.DAT. Sau đó đọc dữ liệu tệp này và in ra màn hình.
PROGRAM VIDU_7_65;
Uses CRT;
Const Tenttin = ‘HOSO.DAT’;
Type Ksvien= Record
Hoten: String[20];
Toan, Ly, Dtb : Real;
end;
KieuTtin = File of Ksvien ;
Var F : KieuTtin ;
X : Ksvien ;
i: Integer;
BEGIN
Clrscr;
Assign(F, TenTtin);
Rewrite(F);
i:=0;
Repeat
Clrscr;
Gotoxy(10,4);
Write(‘NHAP SV THU ‘,i,‘:(Enter đe ket thuc)‘);
With X do
begin
Gotoxy(10,6); Write(‘Ho va ten:’);
Gotoxy(10,8); Write(‘Diem Toan:’);
Gotoxy(10,10); Write(‘Diem Ly :’);
Gotoxy(20,6); Readln(Hoten);
If Hoten<>’’ then
begin
Gotoxy(20,8); Readln(Toan);
Gotoxy(20,10); Readln(Ly);
DTB:=(Toan+Ly)/2;
end;
end;
If X.Hoten<>’’ then
Write(F,X);
i:=i+1;
Until X.Hoten=’’;
Close(F);
Writeln(#32:5,‘Ho va ten’,#32:6,‘TOAN~~~LY~~~~DTB’);
Reset(F);
While Not Eof(F) do
begin
Read(F, X);
Writeln(X.Hoten, #32:20-Length(X.Hoten),
X.Toan:4:1,#32:3, X.Ly:4:1, #32:3, X.DTb:4:1);
end;
Close(F);
Readln;
END.
*Chú ý: Nếu tham số trong chương trình con là tệp thì nó phải là tham số biến, không thể là tham số trị.
Chia sẻ với bạn bè của bạn: |