Nhập/xuất không định dạng được thực hiện với các hàm thành viên istream::read() và
ostream::write().
Hàm istream::read():
istream& read(unsigned char* puch, int nCount); istream& read(signed char* psch, int nCount);
Trích các byte từ dòng cho đến khi giới hạn nCount đạt đến hoặc cho đến khi end- of-file đạt đến. Hàm này có ích cho dòng nhập nhị phân.
Hàm ostream::write():
ostream& write(const unsigned char*puch, int nCount); ostream& write(const signed char*psch, int nCount);
Chèn nCount byte vào từ vùng đệm (được trỏ bởi puch và psch) vào dòng. Nếu file được mởở chế độ
text, các ký tự CR có thểđược chèn vào. Hàm này có ích cho dòng xuất nhị phân. Chẳng hạn:
152 cout.write(Buff,10); Hàm istream::gcount(): int gcount(); Hàm trả về số ký tựđã trích bởi hàm nhập không định dạng cuối cùng. VI. DÒNG NHẬP/ XUẤT FILE
Để thực thi xử lý file trong C++, các chương trình phải include tập tin <iostream.h> và <fstream.h>. Header <fstream.h> gồm định nghĩa cho các lớp dòng ifstream cho nhập (đọc) từ một file, ofstream cho xuất (ghi) tới một file) và fstream cho nhập/xuất (đọc/ghi) tới một file. Các file được mở bằng cách tạo các
đối tượng của các lớp dòng này. Cây phả hệ của các lớp này ở hình 8.2.
• Constructor của lớp ofstream: (1) ofstream();
(2) ofstream(const char* szName,int nMode=ios::out,int nProt=filebuf::openprot);
(3) ofstream(int fd);
(4) ofstream(filedesc fd,char* pch,int nLength);
Trong đó: szName: Tên file được mở.
nMode: Một số nguyên chứa các bit mode định nghĩa là kiểu liệy kê của ios. Có thể kết hợp bằng toán tử
|. Tham số này có thể một trong các giá trị sau:
Mode Ý nghĩa
ios::app Hàm di chuyển con trỏ file tới end-of-file. Khi các byte mới được ghi lên file, chúng luôn luôn nối thêm vào cuối, ngay cả vị trí được di chuyển với hàm
ostream::seekp().
ios::ate Hàm di chuyển con trỏ file tới end-of-file. Khi byte mới đầu tiên được ghi lên file, chúng luôn luôn nối thêm vào cuối, nhưng khi các byte kế tiếp được ghi, chúng ghi vào vị trí hiện hành.
ios::in Mở file đểđọc.Với dòng ifstream, việc mở file đương nhiên được thực hiện ở chế độ này.
ios::out Mở file đểđọc.Với dòng ofstream, việc mở file đương nhiên được thực hiện ở
chếđộ này.
ios::trunc Xóa file hiện có trên đĩa và tạo file mới cùng tên. Cũng có hiểu đây là chặt cụt file cũ, làm cho kích thước của nó bằng 0, chuẩn bị ghi nội dung mới. Mode này được áp dụng nếu ios::out được chỉđịnh và ios::app, ios::ate, và ios::in không được chỉđịnh.
ios::nocreate Nếu file không tồn tại thì thao tác mở thất bại.
ios::noreplace Nếu file tồn tại thì thao tác mở thất bại.
ios::binary Mở file ở chếđộ nhị phân (mặc định là ở chếđộ văn bản).
nProt: Đặc tả chếđộ bảo vệ file.
153
pch: Con trỏ trỏ tới vùng dành riêng chiều dài nLength. Giá trịNULL (hoặc nLength=0) dẫn đến dòng không vùng đệm.
nLength: Chiều dài tính theo byte của vùng dành riêng (0=không vùng đệm). Dạng (1) xây dựng một đối tượng ofstream mà không mở file.
Dạng (2) xây dựng một đối tượng ofstream và mở file đã chỉđịnh.
Dạng (3) xây dựng một đối tượng ofstream và gắn (attach) với một file mở.
Dạng (4) xây dựng một đối tượng ofstream mà liên kết với đối tượng filebuf. Đối tượng filebufđược gắn tới file mở và vùng dành riêng.
• Constructor của lớp ifstream: (1) ifstream();
(2) ifstream(const char* szName,int nMode=ios::in,int nProt=filebuf::openprot);
(3) ifstream(int fd);
(4) ifstream(filedesc fd,char* pch,int nLength);
Dạng (1) xây dựng một đối tượng ifstream mà không mở file. Dạng (2) xây dựng một đối tượng ifstream và mở file đã chỉđịnh.
Dạng (3) xây dựng một đối tượng ifstream và gắn (attach) với một file mở.
Dạng (4) xây dựng một đối tượng ofstream mà liên kết với đối tượng filebuf. Đối tượng filebufđược gắn tới file mở và vùng dành riêng.
• Constructor của lớp fstream: (1) fstream();
(2) fstream(const char* szName,int nMode,int nProt=filebuf::openprot);
(3) fstream(int fd);
(4) fstream(filedesc fd,char* pch,int nLength);
Dạng (1) xây dựng một đối tượng fstream mà không mở file. Dạng (2) xây dựng một đối tượng fstream và mở file đã chỉđịnh.
Dạng (3) xây dựng một đối tượng fstream và gắn (attach) với một file mở.
Dạng (4) xây dựng một đối tượng ofstream mà liên kết với đối tượng filebuf. Đối tượng filebufđược gắn tới file mở và vùng dành riêng.
Nếu chúng ta sử dụng constructor ở dạng (1) thì chúng ta dùng hàm open() để mở file:
• Hàm ofstream::open():
voidopen(constchar* szName,int nMode=ios::out,int nProt=filebuf::openprot);
Hàm ifstream::open():
voidopen(constchar* szName,int nMode=ios::in,int nProt=filebuf::openprot);
Hàm fstream::open():
voidopen(constchar* szName,int nMode,int nProt=filebuf::openprot);
Để đóng file chúng ta dùng hàm close(), hàm này ở các lớp ifstream, ofstream, và fstream đều có dạng:
154
Các hàm liên quan đến con trỏ file:
- Lớp istream: Hàm seekg(): (seek get)
(1) istream& seekg(streampos pos);
(2) istream& seekg(streamoff off,ios::seek_dir dir);
Trong đó:
+ pos: Vị trí mới. streampos là tương đương typedef với long. + off: Giá trị offset mới. là tương đương typedef với long. + dir: hướng seek. Có một trong các trị sau:
ios::begin Seek từ bắt đầu của dòng.
ios::cur Seek tư øvị trí hiện hành của dòng
ios::end Seek từ cuối của dòng Hàm tellg(): (tell get)
streampos tellg();
Hàm trả về vị trí hiện hành của con trỏ file.
- Lớp ostream: Hàm seekp(): (seek put)
(1) ostream& seekp(streampos pos);
(2) ostream& seekp(streamoff off,ios::seek_dir dir);
Hàm tellp(): (tell put)
streampos tellp();
Hàm trả về vị trí hiện hành của con trỏ file.
VI.1. Nhập/xuất file văn bản
Nếu dòng được gắn với file văn bản, việc nhập/xuất file được thực hiện một cách đơn giản bởi các toán tử>> và <<, giống như khi chúng ta làm việc với cin và cout. File văn bản chứa dữ liệu ở dạng mã ASCII, kết thúc bởi ký tự EOF.
Ví dụ 8.28: Tạo file văn bản có thểđược sử dụng trong hệ thống có thể nhận được các tài khoản để giúp
đỡ quản lý tiền nợ bởi các khách hàng tín dụng của công ty. Mỗi khách hàng, chương trình chứa một số tài khoản, tên và số dư (balance).
1: //Chương trình 8.28 2: #include <iostream.h> 3: #include <fstream.h> 4: #include <stdlib.h> 5: 6: int main() 7: {
8: ofstream OutClientFile("clients.dat", ios::out); 9: if (!OutClientFile)
155 11: cerr << "File could not be opened" << endl;
12: exit(1); 13: }
14: cout << "Enter the Account, Name, and Balance." << endl 15: << "Enter EOF to end input." << endl << "? "; 16: int Account;
17: char Name[10]; 18: float Balance;
19: while (cin >> Account >> Name >> Balance) 20: {
21: OutClientFile << Account << " " << Name 22: << " " << Balance << endl; 23: cout << "? "; 24: } 25: OutClientFile.close(); 26: return 0; 27: } Chúng ta chạy ví dụ 8.28, kết quảở hình 8.30 Hình 8.30: Kết quả của ví dụ 8.28 Ví dụ 8.29: Đọc file văn bản tạo ở ví dụ 8.28 và xuất ra màn hình. 1: //Chương trình 8.29 2: #include <iostream.h> 3: #include <fstream.h> 4: #include <iomanip.h> 5: #include <stdlib.h> 6:
7: void OutputLine(int, char*, float); 8:
9: int main() 10: {
11: ifstream InClientFile("clients.dat", ios::in); 12: if (!InClientFile)
13: {
14: cerr << "File could not be opened" << endl; 15: exit(1);
16: }
17: int Account; 18: char Name[10]; 19: float Balance;
20: cout << setiosflags(ios::left) << setw(10) << "Account" 21: << setw(13) << "Name" << "Balance" << endl;
22: while (InClientFile >> Account >> Name >> Balance) 23: OutputLine(Account, Name, Balance);
24: InClientFile.close(); 25: return 0;
156 26: }
27:
28: void OutputLine(int Acct, char *Name, float Bal) 29: {
30: cout << setiosflags(ios::left) << setw(10) << Acct 31: << setw(13) << Name << setw(7) << etprecision(2)
32:<< setiosflags(ios::showpoint | ios::right) << Bal << endl; 33: }
Chúng ta chạy ví dụ 8.29, kết quảở hình 8.31
Hình 8.31: Kết quả của ví dụ 8.29
Do lớp ifstream dẫn xuất từ lớp istream nên chúng ta có thể dùng các hàm istream::get(),
istream::getline(). Ví dụ 8.30: Đọc file văn bản tạo ở ví dụ 8.28 bằng hàm istream::getline() và xuất ra màn hình. 1: //Chương trình 8.30 2: #include <iostream.h> 3: #include <fstream.h> 4: #include <iomanip.h> 5: #include <stdlib.h> 6: 7: #define MAXLINE 256 8: 9: int main() 10: {
11: ifstream InClientFile("clients.dat", ios::in); 12: if (!InClientFile)
13: {
14: cerr << "File could not be opened" << endl; 15: exit(1); 16: } 17: char Line[MAXLINE]; 18: while (!InClientFile.eof()) 19: { 20: InClientFile.getline(Line,MAXLINE-1); 21: cout<<Line<<endl; 22: } 23: InClientFile.close(); 24: return 0; 25: }
Chúng ta chạy ví dụ 8.30, kết quảở hình 8.32.(nội dung tập tin clients.dat)
157
VI.2. Nhập/xuất file nhị phân
Đối với file nhị phân (còn gọi là file truy cập ngẫu nhiên), chúng ta mởở chếđộios::binary. Đối với file nhị phân, chúng ta có thể dùng hàm istream::get() và ostream::put() đểđọc và ghi từng byte một. Để đọc và ghi nhiều byte cùng lúc chúng ta có thể dùng istream::read() và ostream::write().
Ví dụ 8.31: Lấy lại ví dụ 8.28 nhưng lưu dữ liệu dưới dạng nhị phân. 1: //Chương trình 8.31 2: #include <iostream.h> 3: #include <fstream.h> 4: #include <stdlib.h> 5: 6: typedef struct 7: { 8: int Account; 9: char Name[10]; 10: float Balance; 11: }Client; 12: 13: int main() 14: {
15: ofstream OutClientFile("credit.dat", ios::out|ios::binary); 16: if (!OutClientFile)
17: {
18: cerr << "File could not be opened" << endl; 19: exit(1);
20: }
21: cout << "Enter the Account, Name, and Balance." << endl 22: << "Enter EOF to end input." << endl << "? "; 23: Client C;
24: while (cin >> C.Account >> C.Name >> C.Balance) 25: { 26: OutClientFile.write((char *)&C,sizeof(Client)); 27: cout << "? "; 28: } 29: OutClientFile.close(); 30: return 0; 31: }
Chúng ta chạy ví dụ 8.31, kết quảở hình 8.33 (nội dung tập tin credit.dat)
Hình 8.33: Kết quả của ví dụ 8.31 Ví dụ 8.32: Đọc file tạo ở ví dụ 8.31 và xuất ra màn hình. 1: //Chương trình 8.32 2: #include <iostream.h> 3: #include <fstream.h> 4: #include <iomanip.h> 5: #include <stdlib.h> 6:
158 7: typedef struct 8: { 9: int Account; 10: char Name[10]; 11: float Balance; 12: }Client; 13: 14: void OutputLine(Client); 15: 16: int main() 17: {
18: ifstream InClientFile("credit.dat", ios::in|ios::binary); 19: if (!InClientFile)
20: {
21: cerr << "File could not be opened" << endl; 22: exit(1);
23: }
24: cout << setiosflags(ios::left) << setw(10) << "Account" 25: << setw(13) << "Name" << "Balance" << endl; 26: Client C;
27: while (InClientFile.read((char *)&C,sizeof(Client))) 28: OutputLine(C); 29: InClientFile.close(); 30: return 0; 31: } 32: 33: void OutputLine(Client C) 34: {
35: cout << setiosflags(ios::left) << setw(10) << C.Account 36: << setw(13) << C.Name << setw(7) << setprecision(2) 37: << setiosflags(ios::showpoint | ios::right)<< C.Balance << endl; 38: } Chúng ta chạy ví dụ 8.32, kết quảở hình 8.34 Hình 8.34: Kết quả của ví dụ 8.32 BÀI TẬP
Bài 1: Cài đặt toán tử >> và << cho kiểu dữ liệu mảng để nhập và xuất.
Bài 2: Cài đặt thêm toán tử >> và << cho lớp Time trong bài 5 của chương 4.
Bài 3: Cài đặt thêm toán tử >> và << cho lớp Date trong bài 6 của chương 4.
Bài 4: Viết chương trình kiểm tra các giá trị nguyên nhập vào dạng hệ 10, hệ 8 và hệ 16.
Bài 5: Viết chương trình in bảng mã ASCII cho các ký tự có mã ASCII từ 33 đến 126. Chương trình in gồm giá trị ký tự, giá trị hệ 10, giá trị hệ 8 và giá trị hệ 16.
Bài 6: Viết chương trình in các giá trị nguyên dương luôn có dấu + ở phía trước.
Bài 7: Viết chương trình tương tự như lệnh COPY của DOS để sao chép nội dung của file .
Bài 8: Viết chương trình cho biết kích thước file.
159
CHƯƠNG 9
HÀM VÀ LỚP TEMPLATE
Trong phần này, chúng ta tìm hiểu về một trong các đặc tính còn lại của C++, đó là template (khuôn mẫu). Các template cho phép chúng ta đểđịnh rõ, với một đoạn mã đơn giản, một toàn bộ phạm vi của các hàm có liên quan (đa năng hóa)–gọi là các hàm template-hoặc một toàn bộ phạm vi của các lớp có liên quan- gọi là lớp template.
Chúng ta có thể viết một hàm template đơn giản cho việc sắp xếp một mảng và C++ tựđộng phát sinh các hàm template riêng biệt mà sẽ sắp xếp một mảng int, sắp xếp một mảng float, …
Chúng ta có thể viết một lớp template cho lớp stack và sau đó C++ tựđộng phát sinh các lớp template riêng biệt như lớp stack của int, lớp stack của float,…