Viết chương trình:
Nhập từ bàn phím N số thực lưu vào một mảng (N 100 và
N được nhập từ bàn phím).
Sau đó ghi ra một file văn bản có tên là "float.txt" theo quy
cách: dòng đầu tiên lưu số lượng các số thực, các dòng tiếp
theo lưu các số thực, mỗi số lưu trên một dòng.
Đọc lại tệp văn bản đó và lưu các số thực đọc được vào một
mảng.
Sắp xếp các số thực trong mảng theo thứ tự tăng dần và ghi
ra một tệp văn bản khác có tên là "fl
71 trang |
Chia sẻ: huongthu9 | Lượt xem: 486 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Giáo trình Lập trình bằng ngôn ngữ C - Chương 8: Tệp (File) - Ngô Văn Linh, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
1Chương 8
Tệp (FILE)
Ngo Van Linh
Bộ môn Các hệ thống thông tin
Viện Công nghệ thông tin và Truyền thông
Đại học Bách Khoa Hà Nội
2Nội dung
8.1. Giới thiệu
8.2. Kiểu xuất nhập nhị phân và văn bản
8.3. Các hàm thao tác cấp 2
8.4. Đóng/mở tệp, xóa vùng đệm, kiểm tra lỗi
8.5. Nhập xuất ký tự
8.6. Các hàm nhập xuất theo kiểu văn bản
8.7. Tệp văn bản và các thiết bị chuẩn
8.8. Các hàm nhập xuất theo kiểu nhị phân
8.9. Nhập xuất ngẫu nhiên, di chuyển con trỏ chỉ
vị
38.1. Giới thiệu
Một tệp tin đơn giản chỉ là một dãy các byte (mỗi
byte có giá trị từ 0 đến 255) ghi trên đĩa. Số byte
của dãy chính là độ dài của tệp.
Chương này trình bày các thao tác trên tệp như
tạo một tệp mới, ghi dữ liệu từ bộ nhớ lên tệp,
đọc dữ liệu từ tệp vào bộ nhớ,...
Trong C, các thao tác trên tệp được thực hiện
nhờ các hàm thư viện. Các hàm này được chia
thành 2 nhóm: cấp 1 và cấp 2.
Mỗi hàm (cấp 1 hay cấp 2) đều có thể truy xuất
theo cả hai kiểu nhị phân và văn bản.
48.1. Giới thiệu
Các hàm cấp 1:
thực hiện việc đọc/ghi như DOS
Không có dịch vụ xuất nhập riêng cho từng
kiểu dữ liệu mà chỉ có dịch vụ đọc/ghi một dãy
các byte. Ví dụ: để ghi 1 số thực lên đĩa, ta
dùng dịch vụ ghi 4 byte; để ghi 10 số nguyên
lên đĩa, ta dùng dịch vụ ghi 20 byte.
Mỗi tệp có một số hiệu (handle). Các hàm cấp
1 làm việc với tệp thông qua số hiệu tệp này.
58.1. Giới thiệu
Các hàm cấp 2:
được xây dựng từ các hàm cấp 1 nên dễ sử dụng và có nhiều khả
năng hơn.
có dịch vụ truy xuất cho từng kiểu dữ liệu. Ví dụ: hàm xuất nhập
ký tự, chuỗi, số nguyên, số thực, cấu trúc,...
C tự động cung cấp một vùng đệm. Mỗi lần đọc/ghi thường tiến
hành trên vùng đệm chứ không hẳn trên tệp. Khi ghi dữ liệu thì
dữ liệu được đưa vào vùng đệm, khi nào vùng đệm đầy thì dữ liệu
ở vùng đệm mới được đẩy lên đĩa. Khi đọc, thông tin được lấy ra
từ vùng đệm, khi nào vùng đệm trống thì máy mới lấy dữ liệu từ
đĩa đưa vào vùng đệm giảm só lần nhập xuất trên đĩa, nâng
cao tốc độ làm việc.
làm việc với tệp thông qua một biến con trỏ tệp.
68.2. Kiểu nhập xuất nhị phân và văn bản
8.2.1. Kiểu nhị phân
Bảo toàn dữ liệu: trong quá trình xuất nhập,
dữ liệu không bị biến đổi
Mã kết thúc tệp: trong khi đọc, nếu gặp cuối
tệp thì ta nhận được mã kết thúc tệp EOF (giá
trị là -1) và hàm feof cho giá trị khác 0. Tại sao
lại chọn giá trị -1? Lý do rất đơn giản: chưa
gặp cuối tệp thì sẽ đọc được một byte có giá trị
từ 0 đến 255. Giá trị -1 sẽ không trùng với bất
kỳ byte nào.
78.2. Kiểu nhập xuất nhị phân và văn bản
8.2.2. Kiểu văn bản:
chỉ khác kiểu nhị phân khi xử lý ký tự chuyển dòng (mã
10) và ký tự mã 26
Mã chuyển dòng:
khi ghi, một ký tự LF (mã 10) được chuyển thành 2 ký tự CR
(mã 13) và LF.
khi đọc, 2 ký tự liên tiếp CR và LF trên tệp chỉ cho ta một ký
tự LF.
Ví dụ: xét hàm fputc(10,fp);nếu tệp fp mở theo kiểu nhị phân
thì hàm sẽ ghi lên tệp một ký tự mã 10; nhưng nếu fp mở theo
kiểu văn bản thì hàm ghi lên tệp hai mã là 13 và 10.
Mã kết thúc tệp: khi đọc, nếu gặp ký tự có mã 26 hoặc
cuối tệp thì ta nhận được mã kết thúc tệp EOF (số -1)
và hàm feof(fp) cho giá trị khác 0.
88.2.3. Ví dụ minh họa 1
Chương trình sau tạo 2 tệp có tên là vb và
np. Trong chương trình dùng các hàm:
fopen để mở tệp
fputc để ghi một ký tự lên tệp
fclose để đóng tệp
98.2.3. Ví dụ minh họa 1 (tiếp)
#include
void main(){
FILE *fvb, *fnp; //Khai báo 2 biến con trỏ tệp
fvb = fopen("vb","wt"); //Mở tệp vb để ghi theo kiểu văn bản
fnp = fopen("np","wb"); //Mở tệp np để ghi theo kiểu nhị phân
// Ghi các ký tự lên tệp fvb
fputc('A',fvb); fputc(26,fvb); fputc(10,fvb); fputc('B',fvb);
// Ghi các ký tự lên tệp fnp
fputc('A',fnp); fputc(26,fnp); fputc(10,fnp); fputc('B',fnp);
fclose(fvb);
fclose(fnp);
}
10
8.2.3. Ví dụ minh họa 1 (tiếp)
Kết quả:
Tệp vb có các ký tự ứng với các mã: 65 26 13 10 66
Tệp np có các ký tự ứng với các mã: 65 26 10 66
Chú ý:
nếu dùng kiểu văn bản để đọc tệp vb hay tệp np thì ta
chỉ nhận được một ký tự đầu (mã 65) vì khi gặp ký tự
thứ hai (mã 26) thì ta nhận được mã kết thúc tệp.
muốn đọc tất cả các ký tự của tệp, ta cần dùng hàm
fgetc theo kiểu nhị phân.
11
8.2.4. Ví dụ minh họa 2
Xét chương trình sau:
#include
void main(){
FILE *f; // Khai báo biến con trỏ tệp
f = fopen("sl","wt"); //Mở tệp sl để ghi theo kiểu văn bản
// Ghi 3 dòng lên tệp f
fprintf(f,"%2d\n%2d\n%2d",56,7,8);
fclose(f); // Đóng tệp
}
12
8.2.4. Ví dụ minh họa 2 (tiếp)
Hàm fprintf() đưa kết quả ra tệp theo cách như
hàm printf() đưa ra màn hình. Vì tệp f mở theo
kiểu văn bản nên ký tự xuống dòng '\n' được ghi
thành 2 mã 13 và 10. Kết quả là 10 ký tự ứng với
các mã sau được ghi lên tệp:
53 54 13 10 32 55 13 10 32 56
trong đó: 53 là mã của chữ số 5, 54 là mã của
chữ số 6, 13 là CR, 10 là LF, 32 là mã của khoảng
trống, 55 là mã của chữ số 7, 56 là mã của chữ
số 8.
13
8.2.4. Ví dụ minh họa 2 (tiếp)
Nếu dùng trình soạn thảo văn bản (ví dụ
notepad) để mở tệp trên thì ta sẽ nhìn thấy
các số 56, 7, 8 trên 3 dòng khác nhau.
Nếu mở tệp sl theo kiểu nhị phân bằng
cách dùng câu lệnh:
f = fopen("sl","wb");
thì tệp sl sẽ gồm 8 mã sau:
53 54 10 32 55 10 32 56
14
8.3. Các hàm cấp 2
Các hàm dùng chung cho cả 2 kiểu:
fopen dùng để mở tệp.
fclose dùng để đóng tệp.
fcloseall dùng để đóng tất cả các tệp đang mở.
fflush dùng để làm sạch vùng đệm của tệp.
fflushall dùng để làm sạch vùng đệm của các tệp đang mở.
feof cho biết đã gặp cuối tệp hay chưa.
rewind dùng để chuyển con trỏ chỉ vị về đầu tệp.
fseek dùng để di chuyển con trỏ chỉ vị đến bất kỳ vị trí trên tệp
(hàm này chỉ nên dùng cho kiểu nhị phân).
ftell cho biết vị trí hiện tại của con trỏ chỉ vị.
ferror cho biết có lỗi (khác 0) hay không lỗi (=0).
perror thông báo lỗi trên màn hình.
unlink và remove dùng để loại tệp trên đĩa
15
8.3. Các hàm cấp 2 (tiếp)
Các hàm xuất nhập ký tự: dùng cho cả 2 kiểu
putc và fputc dùng để ghi ký tự lên tệp
getc và fgetc dùng để đọc ký tự từ tệp
Các hàm xuất nhập theo kiểu văn bản:
fprintf dùng để ghi dữ liệu theo khuôn dạng lên tệp
fscanf dùng để đọc dữ liệu từ tệp theo khuôn dạng
fputs dùng để ghi một chuỗi ký tự lên tệp
fgets dùng để đọc một dãy ký tự từ tệp
Các hàm xuất nhập theo kiểu nhị phân:
putw dùng để ghi một số nguyên (2 byte) lên tệp
getw dùng để đọc một số nguyên (2 byte) từ tệp
fwrite dùng để ghi một số mẩu tin lên tệp
fread dùng để đọc một số mẩu tin từ tệp
16
8.4. Đóng mở tệp, xóa vùng đệm và kiểm tra lỗi
Dùng chung cho cả 2 kiểu nhị phân và văn
bản
8.4.1. Hàm fopen: Mở tệp
Dạng hàm:
FILE *fopen(const char *tên_tệp, const char *kiểu)
Các đối:
Đối thứ nhất là tên tệp (có thể có đường dẫn đầy
đủ)
Đối thứ hai là kiểu truy nhập, có các giá trị sau:
17
8.4.1. Hàm fopen: Mở tệp
Kiểu Ý nghĩa
"r", "rt" Mở 1 tệp để đọc theo kiểu văn bản. Tệp cần tồn tại nếu
không sẽ có lỗi.
"w", "wt" Mở 1 tệp mới để ghi theo kiểu văn bản. Nếu tệp đã tồn
tại, nó sẽ bị xóa.
"a", "at" Mở 1 tệp để ghi bổ sung theo kiểu văn bản. Nếu tệp chưa
tồn tại thì tạo tệp mới.
"rb" Mở 1 tệp để đọc theo kiểu nhị phân. Tệp cần tồn tại nếu
không sẽ có lỗi.
"wb" Mở 1 tệp mới để ghi theo kiểu nhị phân. Nếu tệp đã tồn
tại, nó sẽ bị xóa.
"ab" Mở 1 tệp để ghi bổ sung theo kiểu nhị phân. Nếu tệp chưa
tồn tại thì tạo tệp mới.
18
8.4.1. Hàm fopen: Mở tệp (tiếp)
Kiểu Ý nghĩa
"r+", "r+t" Mở 1 tệp để đọc/ghi theo kiểu văn bản. Tệp cần tồn tại nếu
không sẽ có lỗi.
"w+", "w+t" Mở 1 tệp mới để đọc/ghi theo kiểu văn bản. Nếu tệp đã tồn
tại, nó sẽ bị xóa.
"a+", "a+t" Mở 1 tệp để đọc/ghi bổ sung theo kiểu văn bản. Nếu tệp
chưa tồn tại thì tạo tệp mới.
"r+b" Mở 1 tệp để đọc/ghi theo kiểu nhị phân. Tệp cần tồn tại nếu
không sẽ có lỗi.
"w+b" Mở 1 tệp mới để đọc/ghi theo kiểu nhị phân. Nếu tệp đã tồn
tại, nó sẽ bị xóa.
"a+b" Mở 1 tệp để đọc/ghi bổ sung theo kiểu nhị phân. Nếu tệp
chưa tồn tại thì tạo tệp mới.
19
8.4.1. Hàm fopen: Mở tệp (tiếp)
Công dụng: hàm dùng để mở tệp. Nếu
thành công, hàm trả về con trỏ kiểu FILE
ứng với tệp vừa mở. Các hàm cấp 2 sẽ làm
việc với tệp thông qua con trỏ này. Nếu có
lỗi hàm trả về giá trị NULL.
Chú ý: Trong các kiểu đọc/ghi, cần làm
sạch vùng đệm trước khi chuyển từ đọc
sang ghi hoặc ngược lại. Dùng các hàm
fflush và di chuyển đầu từ.
20
8.4.2. Hàm fclose: đóng tệp
Dạng hàm: int fclose(FILE *f);
Đối: f là con trỏ tương ứng với tệp cần đóng.
Công dụng: hàm dùng để đóng tệp. Nội dung
đóng tệp gồm:
đẩy dữ liệu còn trong vùng đệm lên đĩa (khi đang ghi)
xóa vùng đệm (khi đang đọc)
giải phóng biến f để nó có thể dùng cho tệp khác. Nếu
thành công, hàm cho giá trị 0, trái lại hàm cho EOF.
21
8.4.3. Hàm fcloseall: đóng các tệp đang mở
Dạng hàm: int fcloseall(void);
Công dụng: hàm dùng để đóng tất cả các
tệp đang mở. Nếu thành công, hàm cho giá
trị nguyên bằng số tệp đóng được, trái lại
hàm cho EOF.
22
8.4.4. Hàm fflush: làm sạch vùng đệm
Dạng hàm: int fflush(FILE *f);
Đối: f là con trỏ tệp
Công dụng: hàm làm sạch vùng đệm của
tệp f. Nếu thành công hàm cho giá trị 0,
trái lại hàm cho EOF.
23
8.4.5. Hàm fflushall: làm sạch vùng đệm
Dạng hàm: int fflushall(void);
Công dụng: hàm dùng làm sạch vùng đệm
của các tệp đang mở. Nếu thành công hàm
cho giá trị nguyên bằng số tệp đang mở,
trái lại hàm cho EOF.
24
8.4.6. Hàm feof: kiểm tra cuối tệp
Dạng hàm int feof(FILE *f);
Đối: f là con trỏ tệp
Công dụng: hàm dùng để kiểm tra cuối tệp.
Hàm cho giá trị khác 0 nếu gặp cuối tệp khi
đọc, trái lại hàm cho giá trị 0.
25
8.4.7. Hàm ferror: kiểm tra lỗi
Dạng hàm: int ferror(FILE *f);
Đối: f là con trỏ tệp
Công dụng: hàm dùng để kiểm tra lỗi thao
tác trên tệp f. Hàm cho giá trị 0 nếu không
lỗi, trái lại hàm cho giá trị khác 0.
26
8.4.8. Hàm perror: thông báo lỗi hệ thống
Dạng hàm: void perror(const char *s);
Đối: s là con trỏ trỏ tới một chuỗi ký tự
Công dụng: hàm in chuỗi s và thông báo lỗi
27
8.4.9. Hàm unlink: xóa tệp
Dạng hàm: int unlink(const char *tên_tệp);
Đối: là tên tệp cần xóa
Công dụng: hàm dùng để xóa 1 tệp trên
đĩa. Nếu thành công, hàm cho giá trị 0, trái
lại hàm cho giá trị EOF.
28
8.4.10. Hàm remove: xóa tệp
Dạng hàm: remove(const char *tên_tệp);
Đối: là tên tệp cần xóa.
Công dụng: hàm dùng để xóa một tệp trên
đĩa. Nó là hàm macro gọi tới unlink.
29
8.4.11. Ví dụ: mở 1 tệp và kiểm tra lỗi
FILE *fp;
/*Mở tệp so_lieu để đọc theo kiểu nhị phân. Nếu
thành công, con trỏ tệp so_lieu gán cho biến fp*/
fp = fopen("so_lieu","rb");
// Kiểm tra lỗi
if(fp==NULL) perror("Lỗi khi mở tệp so_lieu");
30
8.5. Nhập xuất ký tự
Dùng được cả trong kiểu nhị phân và văn bản
nhưng tác dụng khác nhau.
8.5.1. Hàm putc và fputc
Dạng hàm:
int putc(int ch, FILE *fp);
int fputc(int ch, FILE *fp);
Đối: ch là một giá tị nguyên, fp là con trỏ tệp.
Công dụng: hàm ghi lên tệp fp một ký tự có mã bằng:
m = ch%256, trong đó ch được xem là số nguyên
không dấu. Nếu thành công hàm cho mã ký tự được
ghi, trái lại hàm cho EOF
31
8.5.1. Hàm putc và fputc (tiếp)
Ví dụ: câu lệnh putc(-1,fp); sẽ ghi lên tệp
fp mã 255 vì dạng không dấu của -1 là
65535.
Ghi chú:
Hai hàm trên có ý nghĩa như nhau.
Trong kiểu văn bản, nếu m =10 thì hàm sẽ ghi
lên tệp hai mã 13 và 10.
32
8.5.2. Hàm getc và fgetc
Dạng hàm:
int getc(FILE *fp);
int fgetc(FILE *fp);
Đối: fp là con trỏ tệp
Công dụng: hàm đọc 1 ký tự từ tệp fp. Nếu thành công
hàm cho mã đọc được (có giá trị từ 0 đến 255). Nếu gặp
cuối tệp hay có lỗi hàm cho EOF
Ghi chú:
hai hàm trên có ý nghĩa như nhau
trong kiểu văn bản, hàm đọc một lượt cả hai mã 13, 10 và trả về
giá trị 10; khi gặp mã 26 thì hàm không trả về 26 mà trả về EOF
33
8.5.3. Ví dụ
Chương trình sao tệp chế độ nhị phân và dùng hàm fgetc, fputc
#include"stdio.h"
#include"stdlib.h"
void main(){
int c;
char t1[14], t2[14];
FILE *f1, *f2;
printf("\nTỆP NGUỒN:"); gets(t1);
printf("\nTỆP ĐÍCH:"); gets(t2);
f1=fopen(t1,"rb");
if(f1==NULL){
printf("\nTỆP %s không tồn tại",t1); getch(); exit(1);
}
f2=fopen(t2,"wb");
while((c=fgetc(f1))!=EOF) fputc(c,f2);
fclose(f1); fclose(f2);
}
34
8.5.3. Ví dụ (tiếp)
Chương trình trên thực hiện sao tệp theo thuật
toán sau:
bước 1: đọc 1 ký tự của tệp f1, kết quả đặt vào biến c
bước 2: nếu c bằng EOF thì kết thúc; nếu c khác EOF
thì ghi c vào tệp f2 rồi quay trở lại bước 1.
Nhận xét 1: nếu trong chương trình trên, ta thay
bằng kiểu văn bản thì chỉ các byte đứng trước mã
26 đầu tiên của tệp f1 được sao sang tệp f2.
Nhận xét 2: nếu dùng hàm feof và thuật toán:
bước 1: nếu feof(f1) khác 0 thì kết thúc, trái lại chuyển
xuống bước 2.
bước 2: đọc 1 ký tự từ tệp f1, ghi lên tệp f2 thì ta có
đoạn chương trình:
35
8.5.3. Ví dụ (tiếp)
while(!feof(f1)) fputc(fgetc(f1),f2);
Đoạn chương trình này lại chưa thật đúng! Tệp f2 sẽ dài
hơn tệp f1 đúng một byte có giá trị 255.
Lý do: giả sử tệp f1 có đúng một ký tự mã 65, khi đó
thuật toán sẽ diễn ra như sau:
bước 1: đầu từ đang trỏ vào ký tự A nên feof(f1) = 0, chuyển
xuống bước 2.
bước 2: đọc ký tự A của f1 và ghi lên f2, trở lại bước 1.
bước 1: đầu đọc đặt ở cuối tệp f1 nhưng chưa có thao tác đọc
nên feof(f1) vẫn bằng 0, chuyển xuống bước 2.
bước 2: đọc một ký tự của f1. Khi đó nhận được -1. Ghi -1 lên f2
thì mã 255 sẽ được ghi. Ngoài ra, do khi đọc từ f1 gặp phải cuối
tệp nên lúc này feof(f1) khác 0. Đến đây thuật toán kết thúc.
36
8.6. Các hàm nhập xuất theo kiểu văn bản
8.6.1. Hàm fprintf: ghi dữ liệu theo khuôn dạng
Dạng hàm:
int fprintf(FILE *f, const char *dk,...);
Đối:
f là con trỏ tệp
dk chứa địa chỉ của chuỗi điều khiển
... là danh sách các đối mà giá trị của chúng cần ghi lên tệp.
Công dụng: giá trị các đối được ghi lên tệp f theo
khuôn dạng xác định trong chuỗi dk. Nếu thành công
hàm trả về một giá trị nguyên bằng số byte ghi lên tệp,
nếu có lỗi thì trả về EOF.
Nhận xét: Hàm làm việc giống hàm printf.
37
Ví dụ hàm fprintf:
#include
void main(){
FILE *f;
int i;
f=fopen("text","wt");
fprintf(f,"Cac dong");
for(i=1;i<=2;i++) fprintf(f,"\nDong%2d",i);
fclose(f);
}
38
Ví dụ hàm fprintf (tiếp):
Chương trình trên sẽ tạo ra tệp văn bản tên
là text gồm 3 dòng với nội dung như sau:
Cac dong
Dong 1
Dong 2
Dùng trình notepad
mở file text ra xem
nội dung của nó
39
8.6.2. Hàm fscanf: đọc dữ liệu từ tệp theo
khuôn dạng
Dạng hàm:
int fscanf(FILE *f,const char *dk,...);
Đối
f là con trỏ tệp
dk chứa địa chỉ của chuỗi điều khiển
... là danh sách các đối sẽ chứa kết quả đọc được từ
tệp
Công dụng: đọc dữ liệu từ tệp f, biến đổi theo
khuôn dạng trong dk và lưu kết quả vào các đối.
Hàm trả về một giá trị bằng số trường được đọc.
Nhận xét: Hàm làm việc giống hàm scanf.
40
Ví dụ 1 về hàm fscanf
Giả sử có tệp văn bản "da_giac.sl" chứa thông tin
về một đa giác. Tệp gồm n+1 dòng với nội dung
như sau:
Dòng 1: n (số đỉnh)
Dòng 2: x1 y1 (tọa độ đỉnh 1)
Dòng 3: x2 y2 (tọa độ đỉnh 2)
....
Dòng n+1: xn yn (tọa độ đỉnh n)
Chương trình sau sẽ đọc số đỉnh và tọa độ các
đỉnh từ tệp "da_giac.sl"
41
Ví dụ 1 về hàm fscanf (tiếp)
#include
void main(){
FILE *f; int i,n,x[10],y[10];
f=fopen("da_giac.sl","rt");
fscanf(f,"%d",&n);
for(i=1;i<=n;i++) fscanf(f,"%d%d",&x[i],&y[i]);
fclose(f);
// Hiển thị ra màn hình để kiểm tra
printf("%d",n);
for(i=1;i<=n;i++) printf("\n%d %d",x[i],y[i]);
getch();
} Kết quả khi chạy
chương trình
42
Ví dụ 2 về hàm fscanf
Giả sử có một dãy số nguyên ghi trên tệp
văn bản "songuyen.txt". Giữa hai số
nguyên có ít nhất một khoảng trống hay
các dấu xuống dòng. Yêu cầu đọc và in ra
màn hình dãy số nói trên.
Ta phân biệt 2 trường hợp:
Sau chữ số cuối cùng là mã 26 hay cuối tệp
Sau chữ số cuối cùng có ít nhất một khoảng
trống hay các dấu xuống dòng.
43
Ví dụ 2 về hàm fscanf (tiếp)
Trường hợp 1: sau chữ số cuối cùng là mã 26 hay kết thúc tệp
#include
void main(){
FILE *f; int c;
f=fopen("songuyen.txt","r");
while(!feof(f)){
fscanf(f,"%d",&c);
printf("%d\n",c);
}
fclose(f);
getch();
}
File đầu vào
Kết quả hiển
thị
44
Ví dụ 2 về hàm fscanf (tiếp)
Trường hợp 2: sau chữ số cuối cùng có ít nhất một khoảng trống hay
các dấu xuống dòng
#include
void main(){
FILE *f; int c;
f=fopen("songuyen.txt","r");
while(1){
fscanf(f,"%d",&c);
if(feof(f)) break;
printf("%d\n",c);
}
fclose(f);
getch();
}
File đầu vào
Kết quả hiển
thị
45
Phân tích ví dụ 2
Nếu với trường hợp thứ hai mà ta lại dùng
đoạn mã cho trường hợp thứ nhất thì sao?
Kết quả:
Hai số 34Một số
34
46
Phân tích ví dụ 2
Nếu với trường hợp thứ nhất mà ta lại dùng
đoạn mã cho trường hợp thứ hai thì sao?
Kết quả:
Mất số 34
cuối cùng
47
8.6.3. Hàm fputs: ghi một chuỗi ký tự lên tệp
Dạng hàm:
int fputs(const char *s, FILE *f);
Đối:
s là con trỏ trỏ tới địa chỉ đầu của một chuỗi ký tự kết
thúc bằng dấu '\0'.
f là con trỏ tệp.
Công dụng: ghi chuỗi s lên tệp f (dấu '\0' không
ghi lên tệp). Nếu thành công hàm trả về ký tự
cuối cùng được ghi lên tệp; nếu có lỗi hàm trả về
EOF.
48
Ví dụ hàm fputs
Chương trình sau sẽ nhập các dòng ký tự từ bàn phím và ghi lên tệp "vanban"
#include
#include
void main(){
int i=0; char d[256]; FILE *f;
f=fopen("vanban","w");
clrscr();
printf("Bam Enter de ket thuc");
while(1){
i++;
printf("\nDong %d: ",i); gets(d);
if(d[0]=='\0') break; // Bấm Enter để kết thúc
if(i>1) fputc(10,f);
fputs(d,f);
}
fclose(f);
} Tệp
vanban
49
8.6.4. Hàm fgets: đọc một dãy ký tự từ tệp
Dạng hàm:
char *fgets(char *s, int n, FILE *f);
Đối:
s là con trỏ trỏ tới vùng nhớ đủ lớn để chứa chuỗi ký tự sẽ đọc từ tệp.
n là số nguyên xác định độ dài cực đại của dãy cần đọc.
f là con trỏ tệp.
Công dụng: đọc 1 dãy ký tự từ tệp f chứa vào vùng nhớ s. Việc đọc
kết thúc khi:
hoặc đã đọc n-1 ký tự
hoặc gặp dấu xuống dòng (cặp mã 13 10). Khi đó mã 10 được đưa vào
xâu kết quả.
hoặc kết thúc tệp.
Xâu kết quả sẽ được bổ sung thêm dấu hiệu kết thúc chuỗi '\0'. Khi
thành công hàm trả về địa chỉ vùng nhận kết quả; khi có lỗi hoặc gặp
cuối tệp, hàm cho giá trị NULL.
50
Ví dụ hàm fgets
Chương trình đọc các dòng ký tự trên tệp "vanban" và đưa ra màn
hình.
#include
#include
void main(){
int i=0; char d[256]; FILE *f;
f=fopen("vanban","r"); clrscr();
while(!feof(f)){
i++;
fgets(d,256,f);
printf("Dong %d: %s\n",i,d);
}
fclose(f);
getch();
} Kết quả
hiển thị
51
8.7. Tệp văn bản và các thiết bị chuẩn
Có thể dùng các hàm nhập xuất văn bản trên các
thiết bị chuẩn. C đã định nghĩa các tệp tin và con
trỏ tệp ứng với các thiết bị chuẩn như sau:
Tệp Con trỏ Thiết bị
in stdin Thiết bị vào chuẩn (bàn phím)
out stdout Thiết bị ra chuẩn (màn hình)
err stderr Thiết bị lỗi chuẩn (màn hình)
prn stdprn Thiết bị in chuẩn (máy in)
Khi chương trình C bắt đầu làm việc thì các tệp
này được tự động mở, vì vậy có thể dùng các con
trỏ nêu trên để nhập xuất trên các thiết bị chuẩn
52
8.7. .... ví dụ
#include
#include
void main(){
char ht[25]; float diem; int ns;
printf("\nHo ten: ");fgets(ht,25,stdin);
printf("\nDiem va nam sinh");
fscanf(stdin,"%f%d",&diem,&ns);
fputs(ht,stderr);
fprintf(stdout,"Diem %f nam sinh %d",diem, ns);
}
53
8.8. Các hàm nhập xuất theo kiểu nhị phân
8.8.1. Hàm putw: ghi một số nguyên
Dạng hàm: int putw(int n, FILE *f);
Đối:
n là giá trị nguyên
f là con trỏ tệp
Công dụng: ghi giá trị n lên tệp f dưới dạng 2
byte. Nếu thành công hàm trả về số nguyên
được ghi; nếu có lỗi hàm trả về EOF.
54
8.8.2. Hàm getw: đọc một số nguyên
Dạng hàm: int getw(FILE * f);
Đối: f là con trỏ tệp.
Công dụng: đọc một số nguyên 2 byte từ
tệp f. Nếu thành công, hàm trả về số
nguyên đọc được; nếu có lỗi hoặc gặp cuối
tệp, hàm trả về EOF.
55
Ví dụ về hàm putw và getw
Chương trình ghi một dãy số nguyên lên tệp "songuyen", sau đó đọc
các số nguyên từ tệp này và đưa ra màn hình.
#include
#include
void main(){
FILE *f; int i;
// Ghi các số nguyên
f=fopen("songuyen","wb");
for(i=1000;i<=1010;i++) putw(i,f);
fclose(f);
// Đọc các số nguyên
clrscr();
f=fopen("songuyen", "rb");
while((i=getw(f)!=EOF) printf("\n%d",i);
fclose(f);
}
56
8.8.3. Hàm fwrite: ghi các mẫu tin lên tệp
Dạng hàm:
int fwrite(void *ptr, int size, int n, FILE *f);
Đối:
ptr là con trỏ trỏ tới vùng nhớ chứa dữ liệu cần ghi.
size là kích thước của mẫu tin theo byte.
n là số mẫu tin cần ghi.
f là con trỏ tệp.
Công dụng: ghi n mẫu tin kích thước size byte từ
vùng nhớ ptr lên tệp f. Hàm trả về giá trị bằng số
mẫu tin thực sự được ghi.
57
8.8.4. Hàm fread: đọc các mẫu tin từ tệp tin
Dạng hàm:
int fread(void *ptr, int size, int n, FILE *f);
Đối:
ptr là con trỏ trỏ tới vùng nhớ sẽ chứa dữ liệu đọc
được từ tệp tin.
size là kích thước của mẫu tin theo byte.
n là số mẫu tin cần đọc.
f là con trỏ tệp.
Công dụng: đọc n mẫu tin kích thước size byte từ
tệp f chứa vào vùng nhớ ptr. Hàm trả về một giá
trị bằng số mẫu tin thực sự đọc được.
58
Ví dụ về fwrite, fread
Ví dụ 1: sao chép tệp dùng fwrite, fread
#include
#include
void main(){
int n; char t1[20], t2[20], c[1000];
FILE *f1, *f2;
printf("\nTEP NGUON: ");gets(t1);
printf("\nTEP DICH");gets(t2);
f1=fopen(t1,"rb");
if(f1==NULL){
printf("\nTEP %s khong ton tai",t1);
getch(); exit(1);
}
f2=fopen(t2,"wb");
while((n=fread(c,1,1000,f1))>0) fwrite(c,1,n,f2);
fclose(f1); fclose(f2);
}
59
Ví dụ về fwrite, fread
Ví dụ 2: ghi và đọc một dãy n phần tử số thực
#include
#include
void main(){
FILE *f; float a[20],b[20]; int i,n;
// Nhập số phần tử n
do{
printf("Nhap so phan tu n= ");scanf("%d",&n);
}while((n20));
// Nhập vào n phần tử thực
for(i=0;i<n;i++){
printf("\na[%d]= ",i); scanf("%f",&a[i]);
}
60
Ví dụ về fwrite, fread
f=fopen("mangsolieu","wb");
// Ghi n phần tử thực của mảng a vào file f
fwrite(a,sizeof(float),n,f);
fclose(f);
f=fopen("mangsolieu","rb");
// Đọc n phần tử thực từ file f đưa vào mảng b
fread(b,sizeof(float),n,f);
// Hiển thị ra màn hình
for(i=0;i<n;i++)
printf("\nb[%d]=%f",i,b[i]);
fclose(f);
getch();
}
61
Ví dụ về fwrite, fread
Ví dụ 3: ghi và đọc cấu trúc
#include
#include
typedef struct{
char ht[25];
float diem;
}HOCSINH;
void main(){
FILE *f; HOCSINH hs;
// Nhập số liệu từ bàn phím và ghi lên tệp
f=fopen("HOSO.DAT","wb");
printf("Bấm Enter để kết thúc");
while(1){
62
Ví dụ về fwrite, fread
printf("\nHo va ten: ");gets(hs.ht);
if(hs.ht[0]=='\0') break;
printf("\nDiem so: ");scanf("%f",&hs.diem);
fwrite(&hs,sizeof(HOCSINH),1,f);
}
fclose(f);
f=fopen("HOSO.DAT","rb");
while(fread(&hs,sizeof(HOCSINH),1,f)>0)
printf("\n%s %f",hs.ht,hs.diem);
fclose(f);
getch();
}
63
8.9. Nhập xuất ngẫu nhiên và các hàm di
chuyển con trỏ chỉ vị
Mỗi tệp khi đang mở có một con trỏ chỉ vị dùng
để xác định vị trí đọc/ghi trên tệp.
Khi mở tệp tin để đọc/ghi, con trỏ chỉ vị luôn ở
đầu tệp tin. Nhưng nếu mở theo chế độ "a" thì
con trỏ chỉ vị ở cuối tệp để ghi thêm dữ liệu vào
tệp.
Việc xuất nhập dữ liệu được thực hiện từ vị trí
hiện tại của con trỏ chỉ vị và sau khi hoàn thành
thì con trỏ này dịch chuyển đi một số byte bằng
số byte đã đọc hay ghi.
Việc xuất nhập được tiến hành tuần tự từ đầu
đến cuối tệp tin.
64
8.9.1. Hàm rewind: chuyển con trỏ chỉ vị
về đầu tệp
Dạng hàm: void rewind(FILE *f);
Đối: f là con trỏ tệp.
Công dụng: chuyển con trỏ chỉ vị của tệp f
về đầu tệp. Khi đó, việc nhập xuất trên tệp
f được thực hiện từ đầu tệp.
65
8.9.2. Hàm fseek: di chuyển con trỏ chỉ vị
đến vị trí mong muốn
Dạng hàm:
int fseek(FILE *f, long sb, int xp);
Đối:
f là con trỏ tệp.
sb là số byte cần di chuyển.
xp cho biết vị trí xuất phát mà việc dịch chuyển được
bắt đầu từ đấy. xp cps thể nhận các giá trị sau:
SEEK_SET hay 0: xuất phát từ đầu tệp.
SEEK_CUR hay 1: xuất phát từ vị trí hiện tại của con trỏ chỉ vị.
SEEK_END hay 2: xuất phát từ cuối tệp.
66
8.9.2. Hàm fseek (tiếp)
Công dụng: hàm di chuyển con trỏ chỉ vị
của tệp f từ vị trí xác định bởi xp qua một
số byte bằng giá trị tuyệt đối của sb. Chiều
di chuyển về cuối tệp nếu sb dương, trái lại
di chuyển về đầu tệp. Khi thành công, hàm
trả về giá trị 0; nếu có lỗi hàm trả về giá trị
khác 0.
Chú ý: Không nên dùng fseek trên kiểu văn
bản.
67
8.9.3. Hàm ftell: cho biết vị trí hiện tại
của con trỏ chỉ vị
Dạng hàm: long ftell(FILE *f);
Đối: f là con trỏ tệp.
Công dụng: khi thành công, hàm cho biết
vị trí hiện tại của con trỏ chỉ vị (byte thứ
mấy trên tệp f). Số thứ tự byte được tính
từ 0. Khi có lỗi, hàm trả về -1L.
68
Ví dụ: fseek và ftell
Chương trình dùng fseek và ftell xác định độ dài của tệp.
#include
#include
#include
void main(){
FILE *f; long n; char ten[25]; clrscr();
puts("Ten tep: "); gets(ten);
f=fopen(ten,"rb");
if(f==NULL){
printf("\nTep %s khong ton tai",ten);exit(1);
}
fseek(f,0,SEEK_END); n=ftell(f); fclose(f);
printf("\nDo dai cua tep %s là %ld byte",ten,n);
getch();
}
69
Bài tập
Bài 1:
Viết chương trình:
Nhập từ bàn phím N số thực lưu vào một mảng (N 100 và
N được nhập từ bàn phím).
Sau đó ghi ra một file văn bản có tên là "float.txt" theo quy
cách: dòng đầu tiên lưu số lượng các số thực, các dòng tiếp
theo lưu các số thực, mỗi số lưu trên một dòng.
Đọc lại tệp văn bản đó và lưu các số thực đọc được vào một
mảng.
Sắp xếp các số thực trong mảng theo thứ tự tăng dần và ghi
ra một tệp văn bản khác có tên là "floatsx.txt" theo
quy cách giống như tệp "float.txt".
70
Bài tập
Bài 2: Viết chương trình ghép nối nội dung
2 file:
Nhập vào từ bàn phím 2 xâu kí tự là đường dẫn
của file nguồn và file đích
Ghép nội dung của file nguồn vào cuối file đích.
71
Hỏi-đáp
Các file đính kèm theo tài liệu này:
- giao_trinh_lap_trinh_bang_ngon_ngu_c_chuong_8_tep_file_ngo_v.pdf