Ghi dữliệulên tệp 11.1. Lớp ofstream Để ghi dữliệulên tệp chúng ta sử dụng lớp ofstream. Lớp ofstream thừa kế các phương thức của các lớp ios và ostream. Nó cũng thừa kế phương thức: close của lớp fstreambase. Ngoài ra lớp ofstream có thêm các hàm tạo và các phương thức sau: 1. Hàm tạo: ofstream() ; // Không đối dùng để tạo một đối tượng ofstream (dòng xuất), chưa gắn với tệp. 2. Hàm tạo: ofstream(const char *fn, int mode = ios::out, int prot = filebuf::openprot);dùng để tạo một đối tượng ofstream, mở tệp có tên fn để ghi và gắn đối tượng vừa tạo với tệp được mở. + Tham số fn cho biết tên tệp. + Tham số mode có giá trị mặc định là ios::out (mở để ghi). Tham số này có thể là một hợp của các giá trị sau: ios::binary ghi theo kiểu nhị phân (mặc định theo kiểu văn bản) ios::out ghi tệp, nếu tệp đã có thì nó bị xoá ios::app ghi bổ sung vào cuối tệp ios::ate chuyển con trỏ tệp tới cuối tệp sau khi mở tệp ios::trunc xoá nội dung của tệp nếu nó tồn tại ios::nocreate nếu tệp chưa có thì không làm gì (bỏ qua) ios::noreplace nếu tệp đã có thì không làm gì (bỏ qua) + Tham số thứ ba prot quy định cấp bảo vệ của dòng tin, tham số này có thể bỏ qua vì nó đã được gán một giá trị mặc định. 3. Hàm tạo: ofstream(int fd); dùng để tạo một đối tượng ofstream và gắn nó với một tệp có chỉ số fd đang mở. (Để mở và lấy chỉ số (số hiệu) tệp có thể dùng hàm _open, xem cuốn Kỹ thuật Lập trình C của tác giả). 4. Hàm tạo: ofstream(int fd, char *buf, int n); dùng để tạo một đối tượng ofstream , gắn nó với một tệp có chỉ số fd đang mở và sử dùng một vùng nhớ n byte do buf trỏ tới làm bộ đệm. 5. Phương thức: void open(const char *fn, int mode = ios::out, int prot = filebuf::openprot);dùng để mở tệp có tên fn để ghi và gắn nó với đối tượng ofstream. Các tham số của phương thức có cùng ý nghĩa như trong hàm tạo thứ 2. 11.2. Các cách ghitệp 400 Có 2 cách chính sau: + Cách 1: Dùng hàm tạo 2 để xây dựng một dòng xuất, mở một tệp để ghi và gắn tệp với dòng xuất. Sau đó dùng toán tử xuất << và các phương thức để xuất dữliệu ra dòng xuất vừa tạo như thể xuất dữliệu ra cout (xem các mục trên). + Cách 2: Dùng hàm tạo 1 để xây dựng một dòng xuất. Sau đó dùng phương thức open để mở một tệp cụ thể và cho gắn với dòng xuất vừa xây dựng. Khi không cần làm việc với tệp này nữa, chúng ta có thể dùng phương thức close để chấm dứt mọi ràng buộc giữa dòng xuất và tệp. Sau đó có thể gắn dòng xuất với tệp khác. Theo cách này, có thể dùng một dòng xuất (đối tượng ofstream) để xuất dữliệulên nhiều tệp khác nhau. 11.3. Ví dụ Chương trình 1: Chương trình dưới đây sẽ nhập danh sách n thí sinh. Thông tin thí sinh gồm: Họ tên, tỉnh hoặc thành phố cư trú, số báo danh, các điểm toán lý hoá. Dữliệu thí sinh được ghi trên 2 tệp: Tệp DS1.DL ghi thí sinh theo thứ tự nhập từ bàn phím, tệp DS2.DL ghi thí sinh theo thứ tự giảm của tổng điểm. Cấu trúc của 2 tệp như sau: Dòng đầu ghi một số nguyên bằng số thí sinh. Các dòng tiếp theo ghidữliệu của thí sinh. Mỗi thí sinh ghi trên 2 dòng, dòng 1 ghi họ tên trên 24 vị trí và tên tỉnh trên 20 vị trí. Dòng 2 ghi số báo danh (6 vị trí), các điểm toán, lý , hoá và tổng điểm (mỗi điểm ghi trên 6 vị trí trong đó một vị trí chứa phần phân). Chương trình sử dụng lớp TS (Thí sinh) có 3 phương thức: Nhập, sắp xếp và ghi tệp. Cách ghitệp sử dụng ở đây là cách 1: Dùng hàm tạo dạng 2 của lớp ofstream. Chương trình 2 ngay bên dưới cũng giải quyết cùng bài toán nêu trên nhưng sử dụng cách ghitệp thứ 2 (dùng hàm tạo 1 và phương thức open) Một điều đáng nói ở đây là việc nhập một chuỗi ký tự (như họ tên và tên tỉnh) bằng các phương thức get hoặc getline chưa được thuận tiện, vì 2 lý do sau: thứ nhất là các phương thức này có thể bị ký tự chuyển dòng (còn sót trên cin) làm trôi. Thứ hai là các phương thức này có thể để lại một số ký tự trên dòng cin (nếu số ký tự gõ nhiều hơn so với quy định) và các ký tự này sẽ gây ảnh hưởng đến các phép nhập tiếp theo. Để khắc phục các nhược điểm trên, chúng ta đưa vào 2 chương trình trên hàm getstr để nhập chuỗi ký tự từ bàn phím. //CT7_10.CPP // GhiTep #include <iostream.h> #include <iomanip.h> #include <fstream.h> #include <conio.h> #include <stdlib.h> #include <ctype.h> void getstr(char *str,int n) { char tg[21]; while(1) // Bỏ qua Enter và nhập tối đa n-1 ký tự { cin.get(str,n); 402 if (str[0]) break; else cin.ignore(); } while(1) // Loại các ký tự còn lại ra khỏi dòng nhập cin { cin.get(tg,20); if (tg[0]==0) { cin.ignore(); break; } } } struct TSINH { char ht[25]; char ttinh[21]; int sobd; float dt,dl,dh,td; } ; class TS { private: int sots; TSINH *ts; public: TS() { sots=0; ts = NULL; } void nhap(); void sapxep(); void ghitep(char *ttep); }; void TS::nhap() { cout << "\n So thi sinh: " ; cin >> sots ; int n=sots; ts = new TSINH[n+1]; for (int i=1; i<=n; ++i) { cout << "\n Nhap thi sinh thu: " << i << endl; cout << "Ho ten: " ; getstr(ts[i].ht,25); cout << "Tinh hoac thanh pho: " ; getstr(ts[i].ttinh,21); cout << "So bao danh: " ; cin >> ts[i].sobd ; cout << "Cac diem toan, ly, hoa: " ; cin >> ts[i].dt >> ts[i].dl >> ts[i].dh ; ts[i].td =ts[i].dt + ts[i].dl + ts[i].dh ; } } void TS::sapxep() { int n = sots; for (int i=1; i< n; ++i) for (int j=i+1; j<= n; ++j) if (ts[i].td < ts[j].td) { TSINH tg = ts[i]; ts[i] = ts[j]; ts[j] = tg; } } void TS::ghitep(char *ttep) { ofstream f(ttep); f << sots ; f << setprecision(1) << setiosflags(ios::showpoint); for (int i=1; i<=sots; ++i) { f << endl << setw(24) << ts[i].ht << setw(20) << ts[i].ttinh ; 404 f << endl << setw(6) << ts[i].sobd << setw(6) << ts[i].dt << setw(6) << ts[i].dl << setw(6) << ts[i].dh << setw(6) << ts[i].td ; } f.close(); } void main() { clrscr(); TS t; t.nhap(); t.ghitep("DS1.DL"); t.sapxep(); t.ghitep("DS2.DL"); cout << "\n Hoan thanh"; getch(); } Chương trình 2: //CT7_11.CPP // GhiTep #include <iostream.h> #include <iomanip.h> #include <fstream.h> #include <conio.h> #include <stdlib.h> #include <ctype.h> void getstr(char *str,int n) { char tg[21]; while(1) { cin.get(str,n); if (str[0]) break; else cin.ignore(); 406 } while(1) { cin.get(tg,20); if (tg[0]==0) { cin.ignore(); break; } } } struct TSINH { char ht[25]; char ttinh[21]; int sobd; float dt,dl,dh,td; } ; class TS { private: int sots; TSINH *ts; public: TS() { sots=0; ts = NULL; } void nhap(); void sapxep(); void ghitep(char *ttep); }; void TS::nhap() { cout << "\n So thi sinh: " ; cin >> sots ; int n=sots; ts = new TSINH[n+1]; for (int i=1; i<=n; ++i) { cout << "\n Nhap thi sinh thu: " << i << endl; cout << "Ho ten: " ; getstr(ts[i].ht,25); cout << "Tinh hoac thanh pho: " ; getstr(ts[i].ttinh,21); cout << "So bao danh: " ; cin >> ts[i].sobd ; cout << "Cac diem toan, ly, hoa: " ; cin >> ts[i].dt >> ts[i].dl >> ts[i].dh ; ts[i].td =ts[i].dt + ts[i].dl + ts[i].dh ; } } void TS::sapxep() { int n = sots; for (int i=1; i< n; ++i) for (int j=i+1; j<= n; ++j) if (ts[i].td < ts[j].td) { TSINH tg = ts[i]; ts[i] = ts[j]; ts[j] = tg; } } void TS::ghitep(char *ttep) { ofstream f; f.open(ttep,ios::out|ios::noreplace); if (f.bad()) { cout << "\nTep " << ttep << " da ton tai"; cout << "\nCo ghi de? - C/K"; int ch=getch(); if (toupper(ch)=='C') { f.close(); f.open(ttep) ; 408 } else exit(1); } f << sots ; f << setprecision(1) << setiosflags(ios::showpoint); for (int i=1; i<=sots; ++i) { f << endl << setw(24) << ts[i].ht << setw(20) << ts[i].ttinh ; f << endl << setw(6) << ts[i].sobd << setw(6) << ts[i].dt << setw(6) << ts[i].dl << setw(6) << ts[i].dh << setw(6) << ts[i].td ; } f.close(); } void main() { clrscr(); TS t; t.nhap(); t.ghitep("DS1.DL"); t.sapxep(); t.ghitep("DS2.DL"); cout << "\n Hoan thanh"; getch(); } 410 . Ghi dữ liệu lên tệp 11.1. Lớp ofstream Để ghi dữ liệu lên tệp chúng ta sử dụng lớp ofstream. Lớp ofstream. danh, các điểm toán lý hoá. Dữ liệu thí sinh được ghi trên 2 tệp: Tệp DS1.DL ghi thí sinh theo thứ tự nhập từ bàn phím, tệp DS2.DL ghi thí sinh theo thứ tự