Giáo trình C - Chương 3: Nhập và xuất dữ liệu
Tr-ớc đây chúng ta đã xét việc nhập dữ liệu từ bàn phím. Trong nhiều tr-ờng
hợp thực tế , để thuận lợi , chúng ta phải nhập dữ liệu từ các tập tin trên đĩa . Các hàm th-
viện của C cho phép truy cập tập tin và chia là 2 cấp khác nhau :
- các hàm cấp 1 là các hàm ở cấp thấp nhất , truy cập trực tiếp đến các tập tin trên
đĩa.C không cung cấp vùng nhớ đệm cho các hàm này
- các hàm cấp 2 là các hàm truy xuất tập tin cao hơn , do chúng đ-ợc C cung cấp
vùng nhớ đệm
Đối với các hàm cấp 1 , tập tin đ-ợc xem là khối các byte liên tục do đó khi muốn
truy cập mẫu tin cụ thể thì phải tính toán địa chỉ của mẫu tin và nh-vậy công việc vất vả
hơn . Ngoài ra phải cung cấp vùng nhớ đệm cho kiểu đọc ghi này. Đối với các hàm cấp hai
công việc nhẹ nhàng hơn do :
- trình biên dịch tự động cung cấp vùng kí ức đệm cho chúng
- có thể truy xuất các mẫu tin mà không gặp khó khăn nh-với các hàm cấp 1
Trong C , các thông tin cần thiết cho các hàm xuất nhập cấp 2 đ-ợc đặt trong tập tin
stdio.h còn các thông tin về hàm nhập xuất cấp 1 thì ở trong tập tin io.h
tập tin
ngẫu nhiên là :
rewind() : di chuyển con trỏ tập tin về đầu tập tin
Cú pháp : void rewind(FILE *fp);
fseek() : di chuyển con trỏ tập tin về vị trí mong muốn
Cú pháp : int fseek(FILE *fp , long sb , int xp)
fp - con trỏ tập tin
sb - số byte cần di chuyển
38
xp - vị trí xuất phát mà việc dịch chuyển đ−cợ bắt đầu từ đó . xp có thể có các
giá trị sau :
xp=SEEK_SET hay 0 : xuất páht từ đầu tập tin
xp=SEEK_CUR hay 1 : xuất phát từ vị trí con trỏ hiện tại
xp=SEEK_END hay 2 : xuất páht từ cuối tập tin
ftell() : cho biết vị trí hiện tại của con trỏ tập tin
Ta xét ch−ơng trình ví dụ sau :
Ch−ơng trình 3-19 :
#include
#include
#include
void main()
{
struct nguoi {
char ten[30];
int so;
float cao;
}nv;
int recno;
FILE *fp;
long int offset;
clrscr();
if ((fp=fopen("nhanvien.rec","r"))==NULL)
{
printf("Khong mo duoc file\n");
getch();
exit(1);
}
printf("Ban muon doc ban ghi thu may : ");
scanf("%d",&recno);
recno--;
offset=recno*sizeof(nv);
if (fseek(fp,offset,0)!=0)
{
printf("Khong di chuyen duoc con tro file toi do\n");
getch();
exit(1);
}
fread(&nv,sizeof(nv),1,fp);
printf("Ten :%s\n",nv.ten);
printf("Ma nhan vien : %3d\n",nv.so);
printf("Chieu cao :%4.2f\n",nv.cao);
getch();
}
Đ5. Lỗi vào ra
Nói chung , khi mở tập tin thành công ta có thể ghi lên nó . Tuy nhiên , nhiều tr−ờng
hợp không mở đ−ợc tập tin nh−ng ta không biết lỗi do đâu . Để xác định llõi ta dùng hàm
39
ferror() . Hàm này có đối số là con trỏ tập tin . Hàm sẽ có giá trị không nếu không có lỗi gì .
Ng−ợc lại hàm cho giá trị khác không . Ta cũng có thể dùng hàm perror() để chỉ nội dung
lỗi .
Ch−ơng trình 3-20 :
#include
#include
#include
#include
void main()
{
FILE *fp;
char name[40],numstr[10];
int code;
float height;
int n,i;
clrscr();
fp=fopen("a:\newfile.txt","w");
printf("Cho so nguoi can nhap : ");
gets(numstr);
n=atoi(numstr);
for (i=0;i<n;i++)
{
printf("Nhap ten : ");
gets(name);
printf("Nhap ma so : ");
gets(numstr);
code=atoi(numstr);
printf("Nhap chieu cao : ");
gets(numstr);
height=atof(numstr);
fprintf(fp,"%s %d %f",name,code,height);
if (ferror(fp))
{
perror("Loi ghi file ");
getch();
exit(1);
}
}
fclose(fp);
}
Sau lỗi do ta ghi , trình biên dịch sẽ thông báo lỗi cụ thể trong câu “ Loi ghi file : no
such file ỏ directory”
Đ6. Vào ra ở mức hệ thống
1.Các tập tin tiêu đề và biến chuẩn : Trong cách vào ra ở mức hệ thống , ta phải khởi tạo
bộ đệm rồi đặt dữ liệu vào đó tr−ớc ghi hay đọc . Vào ra ở mức hệ thống có lợi ở chỗ l−ợng
mã ít hơn vào ra chuẩn và tốc độ sẽ nhanh hơn . Để dùng các hàm cấp 1 ta phải cần các tập
tin tiêu đề sau :
40
io.h - chứa các prototype của các hàm cấp 1
fcntl.h - chứa các định nghĩa quyền truy cập
sys/stat.h - chá các định nghĩa thuộc tính
dó.h - chứa các thuộc tính theo DOS
2. Tóm tắt các hàm : creat - tạo tập tin mới
_creat - tạo tập tin mới theo kiểu nhị phân
open - mở tập tin
_open - mở tập tin đã tồn tại
close và _close - đóng tập tin
chmod - thay đổi thuộc tính của tập tin
_chmode - thay đổi thuộc tính của tập tin theo kiểu DOS
perror - thông báo lỗi (stdlib.h)
write - ghi một dãy các byte
read - đọc một dãy các byte
lseek - dùng di chuyển con trỏ vị trí
3. Đọc tập tin theo cách vào ra hệ thống : Ta có ch−ơng trình đọc tập tin từ đĩa và hiển thị
lên màn hình theo cách vào ra hệ thống .
Ch−ơng trình 3-21 :
#include
#include
#include
#include
#include
#define BUFFSIZE 512
char buff[BUFFSIZE];
void main(int argc,char *argv[])
{
int inhandle,bytes,i;
clrscr();
if (argc!=2)
{
printf("Dang ");
getch();
exit(1);
}
if ((inhandle=open(argv[1],O_RDONLY|O_BINARY))<0)
{
printf("Khong mo duoc file\n");
getch();
exit(1);
}
while ((bytes=read(inhandle,buff,BUFFSIZE))>0)
for (i=0;i<bytes;i++)
putch(buff[i]);
close(inhandle);
}
4. Khởi tạo bộ đệm : Trong ch−ơng trình ta phải định nghĩa bộ đệm bằng phát biểu
#define BUFFSIZE 512
char buff[BUFFSIZE]
41
Nhờ đó ta đọc đ−ợc dữ liệu từ đĩa vào bộ đệm buff . Với DOS , kích th−ớc bộ đệm nên chọn
là bội số của 512.
5. Mở một tập tin : Cũng giống nh− vào ra bằng hàm cấp 2 , ta phải mở tập tin tr−ớc khi
đọc hay ghi bằng phát biểu :
inhandle=open(argv[1],O_RDONLY | O_BINARY);
Biểu thức này thiết lập sự liên lạc giữa tập tin và hệ điều hành . Trong biểu thức ta cần một
hằng lag oflag là dấu hiệu cho biết mức độ dùng tập tin .
oflag ý nghĩa
O_APPEND Đặt con trỏ ở cuối tập tin
O_CREAT Tạo tập tin mới để ghi(không có hiệu quả nếu tập tin đã có )
O_RDONLY Mở một tập tin để chỉ đọc
O_RDWR Mở một tập tin để chỉ đọc hay ghi
O_TRUNC Mở và cắt bỏ bớt tập tin
O_WRONLY Mở tập tin để ghi
O_BINARY Mở tập tin kiểu nhị phân
O_TEXT Mở tập tin kiểu văn bản
6. Danh số của tập tin : Trong vào ra chuẩn , con trỏ tập tin sẽ nhận đ−ợc ngay khi gọi hàm
fopen() còn trong nhập xuất bằng hàm cấp 1 ta nhậ đ−ợc giá trị nguyên gọi là danh số của
tập tin . Đây là số gán cho một tập tin cụ thể để tham chiếu đến tập tin này . Nếu hàm open()
cho ta giá trị -1 nghĩa là danh số không đúng và phát sinh lỗi .
7. Đọc tập tin vào bộ đệm : Để đọc tập tin vào bộ đệm ta dùng lệnh :
byte = read(inhandle , buff , BUFSIZE);
Hàm này có 3 đối số : danh số của tập tin , địa chỉ của bộ đệm và số byte cực đại cần đọc .
Giá trị của hàm read() chỉ ra số byte đã đọc đ−ợc .
8. Đóng tập tin : Để đóng tập tin ta dùng lệnh
close(inhandle);
9. Thông báo lỗi : Khi hàm open() cho giá trị -1 , nghĩa là có lỗi . Dạng lỗi sẽ đ−ợc đọc
bằng perror() . Ta có ch−ơng trình ví dụ
Ch−ơng trình 3-22 :
#include
#include
#include
#include
#include
#define BUFFSIZE 512
char buff[BUFFSIZE];
void main(int argc,char *argv[])
{
int inhandle,bytes,i;
clrscr();
if (argc!=2)
{
printf("Dang ");
getch();
exit(1);
}
if ((inhandle=open(argv[1],O_RDONLY|O_BINARY))<0)
42
{
perror("Khong mo duoc file\n");
getch();
exit(1);
}
while ((bytes=read(inhandle,buff,BUFFSIZE))>0)
for (i=0;i<bytes;i++)
putch(buff[i]);
close(inhandle);
}
10. Thao tác trên bộ đệm : Việc đ−a tập tin vào bộ đệm có lợi là cho phép truy cập trên bộ
đệm thay vì trên tập tin . Làm nh− vậy nhanh hơn truy cập trên đĩa .
Ch−ơng trình sau cho phép tìm một từ trong một tập tin văn bản .
Ch−ơng trình 3-23 :
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 1024
char buff[BUFFSIZE];
void main(int argc,char *argv[])
{
int inhandle,bytes;
void search(char *,int);
clrscr();
if (argc!=3)
{
printf("Dang ");
getch();
exit(1);
}
if ((inhandle=open(argv[1],O_TEXT))<0)
{
printf("Khong mo duoc file %s\n",argv[1]);
getch();
exit(1);
}
while ((bytes=read(inhandle,buff,BUFFSIZE))>0)
search(argv[2],bytes);
close(inhandle);
printf("Khong tim thay");
getch();
}
void search(char *cau,int buflen)
{
43
char *p,*ptr;
ptr=buff;
while ((ptr=memchr(ptr,cau[0],buflen))!=NULL)
if (memcmp(ptr,cau,strlen(cau))==0)
{
printf("Tu xuat hien lan dau trong cau tai vi tri %d:\n",ptr-buff+1);
for (p=ptr;p<ptr+strlen(cau);p++)
putch(*p);
exit(1);
}
else
ptr++;
}
11. Hàm dùng bộ đệm : Hàm search() là ch−ơng trình con minh hoạ cách dùng bộ đệm . Ta
có một hàm memchr() dạng :
ptr = memchr(ptr , cau[0] , buflen);
Hàm này dùng để tìm vị trí của kí tự cau[0] trong chuỗi chỉ bởi ptr và độ f\dài của phần cần
tìm trong bộ đệm là buflen . Ch−ơng trình sẽ truyền argv[2] cho cau . Hàm này cho giá trị
NULL khi không tìm thấy kí tự cần tìm . Ng−ợc lại nó sẽ cho địa chỉ của kí tự đã tìm thấy
trong bộ đệm . Việc so sánh các chuỗi cau và chuỗi ptr đ−ợc tiến hành nhờ hàm memcmp
trong câu lệnh :
if ((memcmp(ptr,cau,strlen(cau))==0)
Hàm này cũng có 3 đối số là : chuỗi thu nhất ptr , chuỗi thu hai cau và đo dai can so sanh
strlen(cau)
12. Ghi lên tập tin : Ghi thông tin lên tập tin phức tạp hơn đọc từ tập tin . Ta có ch−ơng
trình ví dụ sau dùng để chép từ một tập tin này sang tập tin khác.
Ch−ơng trình 3-24 :
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 4096
char buff[BUFFSIZE];
void main(int argc,char *argv[])
{
int inhandle,outhandle,bytes;
clrscr();
if (argc!=3)
{
printf("Dang ");
getch();
exit(1);
}
if ((inhandle=open(argv[1],O_RDWR|O_BINARY))<0)
{
44
printf("Khong mo duoc file %s\n",argv[1]);
getch();
exit(1);
}
if ((outhandle=open(argv[2],O_CREAT|O_WRONLY|O_BINARY,S_IWRITE))<0)
{
printf("Khong mo duoc file %s\n",argv[2]);
getch();
exit(1);
}
while ((bytes=read(inhandle,buff,BUFFSIZE))>0)
write(outhandle,buff,bytes);
close(inhandle);
close(outhandle);
printf("Da chep xong");
getch();
}
Trong ví dụ trên ta mở một lúc 2 tập tin với danh số là inhamdle và outhandle Biểu
thức mở tập tin nguồn không có gì đặc biệt còn biểu thức mở tập tin đích có dạng :
outhandle = open(argv[2] ,O_CREAT | O_WRONLY | O_BINARY , S_IWRITE)
với O_CREAT để tạo tập tin trên đĩa
O_WRONLY để chỉ ghi lên tập tin
O_BINARY để mở tập tin theo kiểu nhị phân
Khi mở tập tin với O_CREAT , đối thứ 3 của open() là một trong 3 trị :
S_IWRITE : chỉ cho phép ghi lên tập tin
S_IREAD : chỉ cho phép đọc từ tập tin
S_IWRITE | S_IREAD : cho phép đọc và ghi lên tập tin
Để dùng các trị này phải khai báo #include sau khai báo
#include . Hàm write() có đối t−ơng tự nh− read() . Trong vòng lặp while hàm
read() báo số byte đọc đ−ợc qua biến bytes và hàm write() sẽ biết số bytes cần ghi vào tập
tin đích . Trong ch−ơng trình ta dùng bộ đệm với kích th−ớc khá lớn để ch−ơng trình chạy
nhanh .
File đính kèm:
Giao_Trinh_C_Chuong3.pdf

