Bài giảng Ngôn ngữ lập trình C/C++ - Phạm Hồng Thái
Tóm tắt Bài giảng Ngôn ngữ lập trình C/C++ - Phạm Hồng Thái: ...hữ chạy 71 Chương 3. Cấu trúc điều khiển và dữ liệu kiểu mảng không quá nhanh chương trình sử dụng hàm trễ delay(n) (thuộc dos.h, tạm dừng trong n phần nghìn giây) với n được điều chỉnh thích hợp theo tốc độ của máy. Hàm gotoxy(x, y) (thuộc conio.h) trong chương trình đặt con trỏ màn hình ... khi hàm truy nhập đến i thì có nghĩa là i của hàm chứ không phải i của biến ngoài. Dưới đây là ví dụ minh hoạ cho các giải thích trên. Ví dụ 2 : Chúng ta xét lại các hàm luythua() và xmh(). Chú ý rằng trong cả hai hàm này đều có biến i, vì vậy chúng ta có thể khai báo i như một biến ngoài (...NT 3 GOTHIC_FONT 4 • Hướng : hướng viết theo kiểu nằm ngang hay thẳng đứng, tương ứng với các hằng: HOIRIZ_DIR 0 VERT_DIR 1 • Cỡ chữ : Gồm các cỡ chữ đánh số tăng dần từ 1. Cỡ chữ ngầm định là 1. Ví dụ sau lần lượt in tại tâm màn hình tên của các font với các cỡ chữ lớn dần, theo hướ...
dương. Nếu bật trước mỗi số dương sẽ in thêm dấu cộng. − ios::showbase : nếu bật sẽ in số 0 trước các số nguyên hệ 8 và in 0x trước số hệ 16. Nếu tắt (ngầm định) sẽ không in 0 và 0x. − ios::uppercase : nếu bật thì các kí tự biểu diễn số trong hệ 16 (A..F) sẽ viết hoa, nếu tắt (ngầm định) sẽ viết thường. 3. Các bộ và hàm định dạng iostream.h cũng cung cấp một số bộ và hàm định dạng cho phép sử dụng tiện lợi hơn so với các cờ và các phương thức vì nó có thể được viết liên tiếp trên dòng lệnh xuất. a. Các bộ định dạng dec // tương tự ios::dec oct // tương tự ios::dec hex // tương tự ios::hex endl // xuất kí tự xuống dòng ('\n') flush // đẩy toàn bộ dữ liệu ra dòng xuất Ví dụ : cout.setf(ios::showbase) ; // cho phép in các kí tự biểu thị cơ số cout.setf(ios::uppercase) ; // dưới dạng chữ viết hoa int a = 171; int b = 32 ; cout << hex << a << endl << b ; // in 0xAB và 0x20 b. Các hàm định dạng (#include ) setw(n) // tương tự cout.width(n) setprecision(n) // tương tự cout.precision(n) setfill(c) // tương tự cout.fill(c) setiosflags(l) // tương tự cout.setf(l) resetiosflags(l) // tương tự cout.unsetf(l) III. IN RA MÁY IN Như trong phần đầu chương đã trình bày, để làm việc với các thiết bị khác với màn hình và đĩa chúng ta cần tạo ra các đối tượng (thuộc các lớp ifstream, ofstream và fstream) tức các dòng tin bằng các hàm tạo của lớp và gắn chúng với 283 Chương 9. Các dòng nhập/xuất và file thiết bị bằng câu lệnh: ofstream Tên_dòng(thiết bị) ; Ví dụ để tạo một đối tượng mang tên Mayin và gắn với máy in, chúng ta dùng lệnh: ofstream Mayin(4) ; trong đó 4 là số hiệu của máy in. Khi đó mọi câu lệnh dùng toán tử xuất << và cho ra Mayin sẽ đưa dữ liệu cần in vào một bộ đệm mặc định trong bộ nhớ. Nếu bộ đệm đầy, một số thông tin đưa vào trước sẽ tự động chuyển ra máy in. Để chủ động đưa tất cả dữ liệu còn lại trong bộ đệm ra máy in chúng ta cần sử dụng bộ định dạng flush (Mayin << flush << ) hoặc phương thức flush (Mayin.flush(); ). Ví dụ: Sau khi đã khai báo một đối tượng mang tên Mayin bằng câu lệnh như trên Để in chu vi và diện tích hình chữ nhật có cạnh cd và cr ta có thể viết: Mayin << "Diện tích HCN = " << cd * cr << endl; Mayin << "Chu vi HCN = " << 2*(cd + cr) << endl; Mayin.flush(); hoặc : Mayin << "Diện tích HCN = " << cd * cr << endl; Mayin << "Chu vi HCN = " << 2*(cd + cr) << endl << flush; khi chương trình kết thúc mọi dữ liệu còn lại trong các đối tượng sẽ được tự động chuyển ra thiết bị gắn với nó. Ví dụ máy in sẽ in tất cả mọi dữ liệu còn sót lại trong Mayin khi chương trình kết thúc. IV. LÀM VIỆC VỚI FILE Làm việc với một file trên đĩa cũng được quan niệm như làm việc với các thiết bị khác của máy tính (ví dụ như làm việc với máy in với đối tượng Mayin trong phần trên hoặc làm việc với màn hình với đối tượng chuẩn cout). Các đối tượng này được khai báo thuộc lớp ifstream hay ofstream tùy thuộc ta muốn sử dụng file để đọc hay ghi. Như vậy, để sử dụng một file dữ liệu đầu tiên chúng ta cần tạo đối tượng và gắn cho file này. Để tạo đối tượng có thể sử dụng các hàm tạo có sẵn trong hai lớp ifstream và ofstream. Đối tượng sẽ được gắn với tên file cụ thể trên đĩa ngay trong quá trình tạo đối tượng (tạo đối tượng với tham số là tên file) hoặc cũng có thể được gắn với tên file sau này bằng câu lệnh mở file. Sau khi đã gắn một đối tượng với file trên đĩa, có thể sử dụng đối tượng như đối với Mayin hoặc cin, cout. Điều này có nghĩa trong các câu lệnh in ra màn hình chỉ cần thay từ khóa cout bởi tên đối tượng mọi dữ liệu cần in trong câu lệnh sẽ được ghi lên file mà đối tượng đại diện. Cũng tương tự nếu thay cin bởi tên đối tượng, dữ liệu sẽ được đọc vào từ file thay cho từ 284 Chương 9. Các dòng nhập/xuất và file bàn phím. Để tạo đối tượng dùng cho việc ghi ta khai báo chúng với lớp ofstream còn để dùng cho việc đọc ta khai báo chúng với lớp ifstream. 1. Tạo đối tượng gắn với file Mỗi lớp ifstream và ofstream cung cấp 4 phương thức để tạo file. Ở đây chúng tôi chỉ trình bày 2 cách (2 phương thức) hay dùng. + Cách 1: đối_tượng; đối_tượng.open(tên_file, chế_độ); Lớp là một trong hai lớp ifstream và ofstream. Đối tượng là tên do NSD tự đặt. Chế độ là cách thức làm việc với file (xem dưới). Cách này cho phép tạo trước một đối tượng chưa gắn với file cụ thể nào. Sau đó dùng tiếp phương thức open để đồng thời mở file và gắn với đối tượng vừa tạo. Ví dụ: ifstream f; // tạo đối tượng có tên f để đọc hoặc ofstream f; // tạo đối tượng có tên f để ghi f.open("Baitap"); // mở file Baitap và gắn với f + Cách 2: đối_tượng(tên_file, chế_độ) Cách này cho phép đồng thời mở file cụ thể và gắn file với tên đối tượng trong câu lệnh. Ví dụ: ifstream f("Baitap"); // mở file Baitap gắn với đối tượng f để ofstream f("Baitap); // đọc hoặc ghi. Sau khi mở file và gắn với đối tượng f, mọi thao tác trên f cũng chính là làm việc với file Baitap. Trong các câu lệnh trên có các chế độ để qui định cách thức làm việc của file. Các chế độ này gồm có: • ios::binary : quan niệm file theo kiểu nhị phân. Ngầm định là kiểu văn bản. • ios::in : file để đọc (ngầm định với đối tượng trong ifstream). • ios::out : file để ghi (ngầm định với đối tượng trong ofstream), nếu file đã có trên đĩa thì nội dung của nó sẽ bị ghi đè (bị xóa).ios::app : bổ sung vào cuối file • ios::trunc : xóa nội dung file đã có • ios::ate : chuyển con trỏ đến cuối file • ios::nocreate : không làm gì nếu file chưa có 285 Chương 9. Các dòng nhập/xuất và file • ios::replace : không làm gì nếu file đã có có thể chỉ định cùng lúc nhiều chế độ bằng cách ghi chúng liên tiếp nhau với toán tử hợp bit |. Ví dụ để mở file bài tập như một file nhị phân và ghi tiếp theo vào cuối file ta dùng câu lệnh: ofstream f("Baitap", ios::binary | ios::app); 2. Đóng file và giải phóng đối tượng Để đóng file được đại diện bởi f, sử dụng phương thức close như sau: đối_tượng.close(); Sau khi đóng file (và giải phóng mối liên kết giữa đối tượng và file) có thể dùng đối tượng để gắn và làm việc với file khác bằng phương thức open như trên. Ví dụ 2 : Đọc một dãy số từ bàn phím và ghi lên file. File được xem như file văn bản (ngầm định), các số được ghi cách nhau 1 dấu cách. #include #include #include void main() { ofstream f; // khai báo (tạo) đối tượng f int x; f.open("DAYSO"); // mở file DAYSO và gắn với f for (int i = 1; i<=10; i++) { cin >> x; f << x << ' '; } f.close(); } Ví dụ 3 : Chương trình sau nhập danh sách sinh viên, ghi vào file 1, đọc ra mảng, sắp xếp theo tuổi và in ra file 2. Dòng đầu tiên trong file ghi số sinh viên, các dòng tiếp theo ghi thông tin của sinh viên gồm họ tên với độ rộng 24 kí tự, tuổi với độ rộng 4 kí tự và điểm với độ rộng 8 kí tự. #include #include #include #include 286 Chương 9. Các dòng nhập/xuất và file #include #include #include struct Sv { char *hoten; int tuoi; double diem; }; class Sinhvien { int sosv ; Sv *sv; public: Sinhvien() { sosv = 0; sv = NULL; } void nhap(); void sapxep(); void ghifile(char *fname); }; void Sinhvien::nhap() { cout > sosv; int n = sosv; sv = new Sinhvien[n+1]; // Bỏ phần tử thứ 0 for (int i = 1; i <= n; i++) { cout << "\nNhập sinh viên thứ: " << i << endl; cout << "\nHọ tên: "; cin.ignore(); cin.getline(sv[i].hoten); cout > sv[i].tuoi; cout > sv[i].diem; } } 287 Chương 9. Các dòng nhập/xuất và file void Sinhvien::ghi(char fname) { ofstream f(fname) ; f << sosv; f << setprecision(1) << setiosflags(ios::showpoint) ; for (int i=1; i<=sosv; i++) { f << endl << setw(24) << sv[i].hoten << setw(4) << tuoi; f << setw(8) << sv[i].diem; } f.close(); } void Sinhvien::doc(char fname) { ifstream f(fname) ; f >> sosv; for (int i=1; i<=sosv; i++) { f.getline(sv[i].hoten, 25); f >> sv[i].tuoi >> sv[i].diem; } f.close(); } void Sinhvien::sapxep() { int n = sosv; for (int i = 1; i < n; i++) { for (int j = j+1; j <= n; j++) { if (sv[i].tuoi > sv[j].tuoi) { Sinhvien t = sv[i]; sv[i] = sv[j]; sv[j] = t; } } void main() { clrscr(); 288 Chương 9. Các dòng nhập/xuất và file Sinhvien x ; x.nhap(); x.ghi("DSSV1"); x.doc("DSSV1"); x.sapxep(); x.ghi("DSSV2"); cout << "Đã xong"; getch(); } 3. Kiểm tra sự tồn tại của file, kiểm tra hết file Việc mở một file chưa có để đọc sẽ gây nên lỗi và làm dừng chương trình. Khi xảy ra lỗi mở file, giá trị trả lại của phương thức bad là một số khác 0. Do vậy có thể sử dụng phương thức này để kiểm tra một file đã có trên đĩa hay chưa. Ví dụ: ifstream f("Bai tap"); if (f.bad()) { cout << "file Baitap chưa có"; exit(1); } Khi đọc hoặc ghi, con trỏ file sẽ chuyển dần về cuối file. Khi con trỏ ở cuối file, phương thức eof() sẽ trả lại giá trị khác không. Do đó có thể sử dụng phương thức này để kiểm tra đã hết file hay chưa. Chương trình sau cho phép tính độ dài của file Baitap. File cần được mở theo kiểu nhị phân. #include #include #include #include void main() { clrscr(); long dodai = 0; char ch; ifstream f("Baitap", ios::in | ios::binary) ; if (f.bad()) { cout << "File Baitap không có"; exit(1); } 289 Chương 9. Các dòng nhập/xuất và file while (!f.eof()) { f.get(ch)); dodai++; } cout << "Độ dài của file = " << dodai; getch(); } 4. Đọc ghi đồng thời trên file Để đọc ghi đồng thời, file phải được gắn với đối tượng của lớp fstream là lớp thừa kế của 2 lớp ifstream và ofstream. Khi đó chế độ phải được bao gồm chỉ định ios::in | ios::out. Ví dụ: fstream f("Data", ios::in | ios::out) ; hoặc fstream f ; f.open("Data", ios::in | ios::out) ; 5. Di chuyển con trỏ file Các phương thức sau cho phép làm việc trên đối tượng của dòng xuất (ofstream). − đối_tượng.seekp(n) ; Di chuyển con trỏ đến byte thứ n (các byte được tính từ 0) − đối_tượng.seekp(n, vị trí xuất phát) ; Di chuyển đi n byte (có thể âm hoặc dương) từ vị trí xuất phát. Vị trí xuất phát gồm: • ios::beg : từ đầu file • ios::end : từ cuối file • ios::cur : từ vị trí hiện tại của con trỏ. − đối_tượng.tellp(n) ; Cho biết vị trí hiện tại của con trỏ. Để làm việc với dòng nhập tên các phương thức trên được thay tương ứng bởi các tên : seekg và tellg. Đối với các dòng nhập lẫn xuất có thể sử dụng được cả 6 phương thức trên. Ví dụ sau tính độ dài tệp đơn giản hơn ví dụ ở trên. fstream f("Baitap"); f.seekg(0, ios::end); cout << "Độ dài bằng = " << f.tellg(); 290 Chương 9. Các dòng nhập/xuất và file Ví dụ 4 : Chương trình nhập và in danh sách sinh viên trên ghi/đọc đồng thời. #include #include #include #include #include #include #include void main() { int stt ; char *hoten, *fname, traloi; int tuoi; float diem; fstream f; cout > fname; f.open(fname, ios::in | ios::out | ios::noreplace) ; if (f.bad()) { cout << "Tệp đã có. Ghi đè (C/K)?" ; cin.get(traloi) ; if (toupper(traloi) == 'C') { f.close() ; f.open(fname, ios::in | ios::out | ios::trunc) ; } else exit(1); } stt = 0; f << setprecision(1) << setiosflags(ios::showpoint) ; // nhập danh sách while (1) { stt++; cout << "\nNhập sinh viên thứ " << stt ; cout << "\nHọ tên: "; cin.ignore() ; cin.getline(hoten, 25); if (hoten[0] = 0) break; cout > tuoi; 291 Chương 9. Các dòng nhập/xuất và file cout > diem; f << setw(24) << hoten << endl; f << setw(4) << tuoi << set(8) << diem ; } // in danh sách f.seekg(0) ; // quay về đầu danh sách stt = 0; clrscr(); cout << "Danh sách sinh viên đã nhập\n" ; cout << setprecision(1) << setiosflags(ios::showpoint) ; while (1) { f.getline(hoten,25); if (f.eof()) break; stt++; f >> tuoi >> diem; f.ignore(); cout << "\nSinh viên thứ " << stt ; cout << "\nHọ tên: " << hoten; cout << "\nTuổi: " << setw(4) << tuoi; cout << "\nĐiểm: " << setw(8) << diem; } f.close(); getch(); } V. NHẬP/XUẤT NHỊ PHÂN 1. Khái niệm về 2 loại file: văn bản và nhị phân a. File văn bản Trong file văn bản mỗi byte được xem là một kí tự. Tuy nhiên nếu 2 byte 10 (LF), 13 (CR) đi liền nhau thì được xem là một kí tự và nó là kí tự xuống dòng. Như vậy file văn bản là một tập hợp các dòng kí tự với kí tự xuống dòng có mã là 10. Kí tự có mã 26 được xem là kí tự kết thúc file. b. File nhị phân 292 Chương 9. Các dòng nhập/xuất và file Thông tin lưu trong file được xem như dãy byte bình thường. Mã kết thúc file được chọn là -1, được định nghĩa là EOF trong stdio.h. Các thao tác trên file nhị phân thường đọc ghi từng byte một, không quan tâm ý nghĩa của byte. Một số các thao tác nhập/xuất sẽ có hiệu quả khác nhau khi mở file dưới các dạng khác nhau. Ví dụ 1 : giả sử ch = 10, khi đó f << ch sẽ ghi 2 byte 10,13 lên file văn bản f, trong khi đó lệnh này chỉ khi 1 byte 10 lên file nhị phân. Ngược lại, nếu f la file văn bản thì f.getc(ch) sẽ trả về chỉ 1 byte 10 khi đọc được 2 byte 10, 13 liên tiếp nhau. Một file luôn ngầm định dưới dạng văn bản, do vậy để chỉ định file là nhị phân ta cần sử dụng cờ ios::binary. 2. Đọc, ghi kí tự − put(c); // ghi kí tự ra file − get(c); // đọc kí tự từ file Ví dụ 2 : Sao chép file 1 sang file 2. Cần sao chép và ghi từng byte một do vậy để chính xác ta sẽ mở các file dưới dạng nhị phân. #include #include #include #include void main() { clrscr(); fstream fnguon("DATA1", ios::in | ios::binary); fstream fdich("DATA2", ios::out | ios::binary); char ch; while (!fnguon.eof()) { fnguon.get(ch); fdich.put(ch); } fnguon.close(); fdich.close(); } 293 Chương 9. Các dòng nhập/xuất và file 3. Đọc, ghi dãy kí tự − write(char *buf, int n); // ghi n kí tự trong buf ra dòng xuất − read(char *buf, int n); // nhập n kí tự từ buf vào dòng nhập − gcount(); // cho biết số kí tự read đọc được Ví dụ 3 : Chương trình sao chép file ở trên có thể sử dụng các phương thức mới này như sau: #include #include #include #include void main() { clrscr(); fstream fnguon("DATA1", ios::in | ios::binary); fstream fdich("DATA2", ios::out | ios::binary); char buf[2000] ; int n = 2000; while (n) { fnguon.read(buf, 2000); n = fnguon.gcount(); fdich.write(buf, n); } fnguon.close(); fdich.close(); } 4. Đọc ghi đồng thời #include #include #include #include #include 294 Chương 9. Các dòng nhập/xuất và file #include #include #include struct Sv { char *hoten; int tuoi; double diem; }; class Sinhvien { int sosv; Sv x; char fname[30]; static int size; public: Sinhvien(char *fn); void tao(); void bosung(); void xemsua(); }; int Sinhvien::size = sizeof(Sv); Sinhvien::Sinhvien(char *fn) { strcpy(fname, fn) ; fstream f; f.open(fname, ios::in | ios::ate | ios::binary); if (!f.good) sosv = 0; else { sosv = f.tellg() / size; } } void Sinhvien::tao() 295 Chương 9. Các dòng nhập/xuất và file { fstream f; f.open(fname, ios::out | ios::noreplace | ios::binary); if (!f.good()) { cout << "danh sach da co. Co tao lai (C/K) ?"; char traloi = getch(); if (toupper(traloi) == 'C') return; else { f.close() ; f.open(fname, ios::out | ios::trunc | ios::binary); } } sosv = 0 while (1) { cout << "\nSinh viên thứ: " << sosv+1; cout << "\nHọ tên: "; cin.ignore(); cin.getline(x.hoten); if (x.hoten[0] == 0) break; cout > x.tuoi; cout > x.diem; f.write((char*)(&x), size); sosv++; } f.close(); } void Sinhvien::bosung() { fstream f; f.open(fname, ios::out | ios::app | ios::binary); if (!f.good()) { cout << "danh sach chua co. Tao moi (C/K) ?"; char traloi = getch(); if (toupper(traloi) == 'C') return; else { 296 Chương 9. Các dòng nhập/xuất và file f.close() ; f.open(fname, ios::out | ios::binary); } } int stt = 0 while (1) { cout << "\nBổ sung sinh viên thứ: " << stt+1; cout << "\nHọ tên: "; cin.ignore(); cin.getline(x.hoten); if (x.hoten[0] == 0) break; cout > x.tuoi; cout > x.diem; f.write((char*)(&x), size); stt++; } sosv += stt; f.close(); } void Sinhvien::xemsua() { fstream f; int ch; f.open(fname, ios::out | ios::app | ios::binary); if (!f.good()) { cout << "danh sach chua co"; getch(); return; } cout << "\nDanh sách sinh viên" << endl; int stt ; while (1) { cout << "\nCần xem (sua) sinh viên thứ (0: dừng): " ; cin >> stt; if (stt sosv) break; f.seekg((stt-1) * size, ios::beg); 297 Chương 9. Các dòng nhập/xuất và file f.read((char*)(&x), size); cout << "\nHọ tên: " << x.hoten; cout << "\nTuổi: " << x.tuoi; cout << "\nĐiểm: " << x.diem; cout << "Có sửa không (C/K) ?"; cin >> traloi; if (toupper(traloi) == 'C') { f.seekg(-size, ios::cur); cout << "\nHọ tên: "; cin.ignore(); cin.getline(x.hoten); cout > x.tuoi; cout > x.diem; f.write((char*)(&x), size); } } f.close(); } void main() { int chon; Sinhvien SV("DSSV") ; while (1) { clrscr(); cout << "\n1: Tạo danh sách sinh viên"; cout << "\n2: Bổ sung danh sách"; cout << "\n3: Xem – sửa danh sách"; cout << "\n0: Kết thúc"; chon = getch(); chon = chon – 48; clrscr(); if (chon == 1) SV.tao(); else if (chon == 2) SV.bosung(); else if (chon == 3) SV.xemsua(); else break; 298 Chương 9. Các dòng nhập/xuất và file } } BÀI TẬP 1. Viết chương trình đếm số dòng của một file văn bản. 2. Viết chương trình đọc in từng kí tự của file văn bản ra màn hình, mỗi màn hình 20 dòng. 3. Viết chương trình tìm xâu dài nhất trong một file văn bản. 4. Viết chương trình ghép một file văn bản thứ hai vào file văn bản thứ nhất, trong đó tất cả chữ cái của file văn bản thứ nhất phải đổi thành chữ in hoa. 5. Viết chương trình in nội dung file ra màn hình và cho biết tổng số chữ cái, tổng số chữ số đã xuất hiện trong file. 6. Cho 2 file số thực (đã được sắp tăng dần). In ra màn hình dãy số xếp tăng dần của cả 2 file. (Cần tạo cả 2 file dữ liệu này bằng Editor của C++). 7. Viết hàm nhập 10 số thực từ bàn phím vào file INPUT.DAT. Viết hàm đọc các số thực từ file trên và in tổng bình phương của chúng ra màn hình. 8. Viết hàm nhập 10 số nguyên từ bàn phím vào file văn bản tên INPUT.DAT. Viết hàm đọc các số nguyên từ file trên và ghi những số chẵn vào file EVEN.DAT còn các số lẻ vào file ODD.DAT. 9. Nhập bằng chương trình 2 ma trận số nguyên vào 2 file văn bản. Hãy tạo file văn bản thứ 3 chứa nội dung của ma trận tích của 2 ma trận trên. 10. Tổ chức quản lý file sinh viên (Họ tên, ngày sinh, giới tính, điểm) với các chức năng : Nhập, xem, xóa, sửa, tính điểm trung chung. 11. Thông tin về một nhân viên trong cơ quan bao gồm : họ và tên, nghề nghiệp, số điện thoại, địa chỉ nhà riêng. Viết hàm nhập từ bàn phím thông tin của 7 nhân viên và ghi vào file INPUT.DAT. Viết hàm tìm trong file INPUT.DAT và in ra thông tin của 1 nhân viên theo số điện thoại được nhập từ bàn phím. 299
File đính kèm:
- bai_giang_ngon_ngu_lap_trinh_cc_pham_hong_thai.pdf