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