4/13/2010
1
Chương 6. Xử lý bộ nhớ ngoài
ĐỖ BÁ LÂM
ViỆN CNTT&TT, TRƯỜNG ĐHBK HÀ NỘI
2
Nội dung
6.1. Khái niệm và phân loại tệp
6.2. Các thao tác với tệp
6.2.1. Khai báo
6.2.2. Mở tệp
6.2.3. Đóng tệp
6.2.4. Truy nhập tệp nhị phân
6.2.5. Truy nhập tệp văn bản
3
6.1. Khái niệm và phân loại tệp
Khái niệm
Tệp dữ liệu (file) là một tập hợp các dữ liệu có
liên quan đến nhau và có cùng kiểu dữ liệu
Được lưu trên các thiết bị nhớ ngoài
Biểu tượng tệp tin
4/13/2010
2
4
6.1. Khái niệm và phân loại tệp
Phân loại
Tệp văn bản (text file)
• Các phần tử là các kí tự: chữ cái, chữ số, dấu câu, dấu
cách, kí tự điều khiển
• Kí tự điều khiển: kí tự về đầu dòng (mã ASCII là 10), kí
tự xuống dòng (mã ASCII là 13)
Tệp nhị phân (binary file)
• Các phần tử là các số nhị phân 0, 1 mã hóa thông tin.
• Thông tin được mã hóa: số nguyên, kí tự…
• Tệp văn bản là trường hợp riêng của tệp nhị phân
5
6.1. Khái niệm và phân loại tệp
Ý nghĩa của tệp
Cất giữ dữ liệu lâu dài
Phân biệt tệp và mảng
Giống: tập hợp các phần tử cùng kiểu
Khác
• Mảng: lưu trữ ở bộ nhớ trong, kích thước bị hạn
chế
• Tệp: lưu trữ ở bộ nhớ ngoài, kích thước có thể lớn
hơn mảng rất nhiều
6
6.1. Khái niệm và phân loại tệp
Tổ chức
Phần tử kết thúc tệp: EOF (End Of File
idicator)
EOF: -1 trong stdio.h
Con trỏ tệp: con trỏ xác định vị trí đang làm
việc của tệp
E O F. . . . .
Tên tệp OS
Phần tử dữ liệu
đầu tiên
Phần tử dữ liệu
cuối cùng
Phần tử kí hiệu
kết thúc tệp
Con trỏ vị trí đang
làm việc của tệp
4/13/2010
3
7
6.1. Khái niệm và phân loại tệp
Quy trình thao tác với tệp
Khai báo tệp
Mở tệp để làm việc
Truy nhập tệp
Đóng tệp
8
6.2. Các thao tác với tệp
6.2.1. Khai báo
Truy nhập tệp thông qua con trỏ tệp
Tại sao lại là con trỏ?
Cú pháp:
FILE *tên_con_trỏ_tệp;
Ví dụ: FILE * f1, * f2;
9
6.2.2. Mở tệp
Cú pháp
tên_con_trỏ_tệp = fopen(tên_tệp, chế_độ_mở_tệp);
Chế độ mở tệp
Phụ thuộc vào mục đích sử dụng tệp: read (r),
write (w), read write…
Loại tệp: văn bản (t), nhị phân (b)
4/13/2010
4
10
6.2.2. Mở tệp
Mục đích sử dụng
Kí hiệu Mục đích sử dụng
“r” Mở tệp đã có để đọc. Báo lỗi nếu tệp không tồn tại
“w” Mở tệp mới để ghi. Nếu tệp đã có thì xóa hết nội dung cũ
“a” Mở tệp để ghi dữ liệu vào cuối. Nếu chưa có sẽ tạo tệp mới
“r+” Mở tệp để vừa đọc, vừa ghi. Báo lỗi nếu tệp không tồn tại
“w+” Mở tệp để vừa đọc, vừa ghi. Nếu tệp đã có thì xóa hết nội dung
cũ.
“a+” Mở tệp để đọc và ghi dữ liệu vào cuối. Nếu chưa có sẽ tạo mới
11
6.2.2. Mở tệp
Bản chất dữ liệu của tệp
Ví dụ: FILE * f1, * f2, *f3;
Để mở tệp c:\abc.txt để đọc ta dùng lệnh
f1 = fopen("c:\\abc.txt", "rt");
Để mở tệp c:\ho_so.dat để ghi ta dùng lệnh
f2 = fopen("c:\\ho_so.dat", "wt");
Để mở tệp c:\abc.txt để vừa đọc và ghi ta dùng lệnh
f3 = fopen("c:\\abc.txt", "r+t");
Kí hiệu Bản chất dữ liệu của tệp
“b” Tệp nhị phân
“t” Tệp văn bản
12
6.2.2. Mở tệp
Chú ý:
Trong C ngầm định là tệp văn bản. Do vậy có thể bỏ
qua “t” trong chế độ mở tệp nếu mở tệp văn bản
Để bắt lỗi mở tệp không thành công
if((con_trỏ_tệp = fopen(tên_tệp, chế_độ_mở_tệp))
==NULL){
<Xử lí cho trường hợp mở tệp không thành công>
}else // Trường hợp mở tệp thành công
{
<Xử lí khi mở tệp thành công>
}
4/13/2010
5
13
6.2.3. Đóng tệp
Đảm bảo những thay đổi dữ liệu được ghi lại
trên tệp
int fclose(FILE* <tên con trỏ tệp>);
Thành công: 0
Ngược lại: EOF
14
6.2.4. Truy nhập tệp văn bản
Ghi dữ liệu lên tệp
Sử dụng: fprintf(), fputs(), putc()
fprintf()
int fprintf(FILE* con_trỏ_tệp, xâu_định_dạng,
[danh_sách_tham_số]);
Khác: printf in ra thiết bị ra chuẩn là màn hình (stdout),
fprintf() phải chỉ ra tên tệp ghi dữ liệu.
Thành công: số bytes ghi dữ liệu
Thất bại: EOF
Ví dụ: fprintf(fptr, “%d”, a);
6.2.4. Truy nhập tệp văn bản
fputs()
int fputs(char* xâu_kí_tự, FILE* con_trỏ_tệp);
Ghi nội dung của xâu_kí_tự lên con_trỏ_tệp.
Khác puts ở chỗ ghi puts thêm kí tự xuống
dòng
Thành công: kí tự cuối cùng được ghi
Thất bại: EOF
Ví dụ fputs(s,fptr);
15
4/13/2010
6
16
6.2.3. Truy nhập tệp văn bản
putc()
int putc(int ch, FILE* con_trỏ_tệp);
Ghi kí tự được chứa trong biến ch lên tệp
Thành công: số nguyên là mã ASCII của kí tự
Ngược lại: EOF
Ví dụ: putc(„a‟,fptr);
Demo: dF.c
17
6.2.4. Truy nhập tệp văn bản
Đọc dữ liệu từ tệp.
Sử dụng hàm: fscanf(), fgets(), fgetc(), getc();
fscanf()
int fscanf(FILE* con_trỏ_tệp, xâu_định_dạng,
[danh_sách_địa_chỉ]);
Đọc dữ liệu từ tệp: con trỏ tệp
Định dạng đọc: xâu định dạng; Lưu vào dsách địa chỉ
Thành công: số byte đọc được. Thất bại: EOF
Ví dụ: fscanf(fptr, “%d %c”,&a, &c);
Trước khi sử dụng fscanf() nên dùng lệnh
fflush(con_trỏ_tệp)
18
6.2.4. Truy nhập tệp văn bản
fgets()
char* fgets(char* xâu_kí_tự, int n, FILE* con_trỏ_tệp);
Đọc từ tệp một xâu kí tự và gán cho biến xâu_kí_tự
Việc đọc dừng khi đọc được đủ n-1 kí tự hoặc gặp
dấu xuống dòng
Thành công: xâu kí tự được trỏ bởi xâu_kí_tự.
Thất bại: trả về con trỏ NULL
Ví dụ: fgets(hoten, 20, fptr);
4/13/2010
7
19
6.2.4. Truy nhập tệp văn bản
getc()
int getc(FILE* con trỏ tệp);
Đọc một kí tự từ tệp và trả về một số nguyên
tương ứng
Thành công: kí tự được đọc (dạng int)
Thất bại: trả về EOF
Demo: dF.c
20
6.2.4. Truy nhập tệp văn bản
feof()
int feof(FILE* con_trỏ_tệp);
Kiểm tra xem đã duyệt đến cuối tệp hay chưa
Kiểm tra phần tử EOF đã được đọc trong lần
đọc gần nhất hay chưa
E O FE O FCác phần tử của tệp Các phần tử của tệp
Con trỏ vị trí đang
làm việc của tệp
Con trỏ vị trí đang
làm việc của tệp
Chưa đọc phần tử EOF
feof() = 0
Đã đọc phần tử EOF
feof() = 1
21
6.2.4. Truy nhập tệp văn bản
fseek()
int fseek(FILE* con_trỏ_tệp, long int n, int
vị_trí_ban_đầu);
Di chuyển con trỏ tệp từ vị_trí_bắt_đầu đi n
bytes
Thành công: trả về 0 nếu thành công, ngược
lại trả về khác 0.
n>0: dịch chuyển về cuối tệp, n<0: dịch
chuyển về đầu tệp.
4/13/2010
8
22
6.2.4. Truy nhập tệp văn bản
Vị trí bắt đầu
Ví dụ
fseek(fptr, 50, SEEK_SET);
fseek(fptr, - 40, 2);
Tên hằng Giá trị Ý nghĩa
SEEK_SET 0 Vị trị bắt đầu là tệp
SEEK_CUR 1 Vị trí bắt đầu là vị trí hiện thời
của con trỏ tệp
SEEK_END 2 Vị trí bắt đầu là cuối tệp
23
6.2.4. Truy nhập tệp văn bản
rewind()
void rewind(FILE* con_trỏ_tệp);
Đưa con trỏ về đầu tệp
Tương đương với fseek(fptr, 0, SEEK_SET);
Chú ý: để sử dụng các hàm fscanf(), fgets(),
getc(), fflush(), fprintf(), fputs(), putc(), feof(),
fseek() và rewind() ta cần khai báo tệp tiêu đề
stdio.h.
6.2.4. Truy nhập tệp văn bản
Đọc
fscanf()
fgets()
getc()
Thất bại
Trả về EOF (trừ fgets)
Hàm fgetc và getc
tương đương nhau
Ghi
fprintf
fputs
putc
Thất bại
Trả về EOF
Hàm fputc và putc
tương đương nhau
24
4/13/2010
9
25
6.2.5. Truy nhập tệp nhị phân
Ghi dữ liệu
int fwrite(void *địa_chỉ_biến, int số_byte, int
số_mục, FILE *con trỏ tệp);
Đọc một vùng dữ liệu có địa chỉ bắt đầu là
địa_chỉ_biến và có kích thước số_byte *
số_mục bytes rồi ghi lên tệp
Thành công: số mục ghi lên tệp
Thất bại: trả về 0.
26
6.2.5. Truy nhập tệp nhị phân
Đọc dữ liệu trên tệp
int fread(void *địa_chỉ_biến, int số_byte, int
số_mục, FILE *con_trỏ_tệp);
Đọc một khối dữ liệu kích thước số_byte *
số_mục bytes rồi ghi lên vùng nhớ có địa chỉ
là địa_chỉ_biến.
Thành công: trả về số mục đọc được
Thất bại: trả về 0
Demo: dFb.c
27
6.2.5. Truy nhập tệp nhị phân
Dịch chuyển con trỏ tệp
Sử dụng fseek() và rewind() như với tệp văn bản
Ghi nhớ các cặp hàm có chức năng đối ngẫu
nhau
• fread() – fwrite()
• fscanf() – fprintf()
• fputs() – fgets()
• getc() – putc()
Thất bại trong tệp văn bản: EOF, tệp nhị phân: 0
trừ fgets trả về NULL
4/13/2010
10
So sánh tệp văn bản và tệp nhị phân
Khác nhau ở mã chuyển dòng và ký tự mã 26
Mã chuyển dòng
Tệp nhị phân: ghi ký tự mã 10 (LF)
Tệp văn bản: ghi 2 ký tự mã 13 (CR) và 10
(LF). Khi đọc sẽ kết hợp 2 ký tự thành LF.
Ký tự mã 26
Tệp văn bản: khi gặp ký tự mã 26 hoặc cuối tệp
=> EOF
Tệp nhị phân: cuối tệp => EOF
Demo dBinaryText.c
28
Lỗi cần tránh
Sử dụng hàm feof chưa đúng
Hiện tượng: hiển thị (đọc) thừa dữ liệu cuối
cùng
Ví dụ dErrorEOF.c
Cách khắc phục
Phổ biến: sử dụng các hàm đọc dữ liệu. Đọc
chừng nào còn thành công
29
Nên sử dụng
Sử dụng tệp nhị phân khi có thể
Ưu điểm
Không bị lỗi khi gặp ký tự mã 26
Hỗ trợ nhiều hàm đọc/ghi hơn tệp văn bản
Hàm fread, fwrite cho phép đọc/ghi cùng lúc
một cấu trúc/mảng các phần tử cùng kiểu
30
4/13/2010
11
31
Ví dụ tổng hợp
Nhập từ bàn phím một mảng các số thực. Thực
hiện lần lượt các công việc (làm với cả tệp văn bản
và nhị phân). Demo dEx.c
Ghi các phần tử của mảng vào tệp sothuc1.dat
Đọc lần lượt các phần tử trong tệp sothuc1.dat
và ghi các số thực lớn hơn 5 sang tệp
sothuc2.dat
Nhập từ bàn phím số thứ tự của phần tử muốn
hiển thị trong tệp sothuc2.dat. Sau đó hiển thị
giá trị phần tử này ra màn hình
32
Thảo luận