Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 13 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
13
Dung lượng
156,59 KB
Nội dung
AllaboutFileI/OinC++ ( Bài này mình lấy của anh CUONGUYEN ở trên ddth.com, bài này được viết từ năm 2002. Mình thấy khá hay nên tổng hợp lại cho các bạn dễ tham khảo. Mình trích nguyên văn, chỉ thêm style thôi. Bài viết có 5 phần: 1/ Write to file 2/ Đọc từ file 3/ Quản lý luồng I/O 4/ Kiểm tra cờ trạng thái 5/ Xử lí file nhị phân 6/ Một số hàm hữu dụng ) Tôi post bài này lên không dám qua mặt các “cao thủ”, mà chỉ để nói rằng trong diễn đ àn rất cần các bài post này để mọi ngư ời cùng học, và t ôi hi vọng là các cao thủ sẽ post các bài của mình lên để cho tôi học hỏi. Bài này sẽ giới thiệu về File I/O. 1. Write To File. Chúng ta cùng xem chương trình đầu tiên nhé: #include <fstream.h> void main() { ofstream WriteToFile(“first.txt”); WriteToFile << “Hello World!”; WriteToFile.close(); } Chương trình này tạo một file “first.txt” (ở nơi cùng thư mục với file chương trình của bạn), rồi ghi dòng “Hello World!” vào file. Hãy đi từng dòng nhé: #include <fstream.h> - Thư viện để sử dụng các hàm File I/O. ofstream WriteToFile(“first.txt”): ofstream là một lớp. Dòng này tạo một đối tượng thể hiện lớp ofstream là WriteToFile (hoặc một tên bất kỳ), dùng hàm contructor với tham số “first.txt”. Hàm này sẽ tạo ra một file với tên là “first.txt”. WriteToFile << “Hello World!”: ghi chuỗi “Hello World!” vào file. WriteToFile.close(): đóng file lại. Mỗi khi bạn mở một file, sau đó bạn phải đóng nó lại. 2. Đọc từ File. Chương trình. #include <fstream.h> void main(){ ifstream OpenFile("first.txt"); char ch; while(!OpenFile.eof()) { OpenFile.get(ch); cout << ch; } OpenFile.close(); } Chương trình này dùng cách đọc từng ký tự (còn có cách khác là đọc từng từ (word) và đọc từng dòng (line) - sẽ tham khảo sau). ifstream OpenFile(“first.txt”) – Tạo một đối tượng thể hiện lớp ifstream tên là OpenFile (có thể dùng tên khác tuỳ ý). Hàm contructor với tham số “first.txt”, đây là file mà chúng ta đã tạo ở mục trên. Hàm này sẽ mở file “first.txt” để đọc. char ch: dòng này tôi biết rằng các bạn đã quá quen thuộc. while(!OpenFile.eof()) - hàm eof() của lớp ifstream. Hàm eof() trả về giá trị 0 khi chưa kết thúc file, ngược lại là 1. OpenFile.get(ch); - get(char &ch) là một hàm của lớp ifstream, hàm này sẽ đọc từng ký tự của file và gán cho biến ch. Sau đó sẽ chỉ đến ký tự tiếp theo (do inside-pointer) Vòng lặp while sẽ lặp cho tới khi kết thúc file, mỗi lần lặp sẽ đọc một ký tự và gán cho biến ch. Sau đó đưa ra màn h ình ký tự đó: cout<<ch; Vòng lặp tiếp theo sẽ đọc ký tự tiếp theo sau ký tự trước (do inside-pointer: điều này sẽ được giải thích ở phần sau). Do đó khi chạy chương trình, sẽ xuất hiện trên màn hình dòng chữ: “Hello World!” 3. Quản lý Iuồng I/O. Trong phần này chúng ta sẽ học về một số hàm hữu dụng. Phần trước chúng ta đã tạo một file bằng lệnh: ofstream File(“filename.txt”); Chúng ta có một cách khác đó là sử dụng hàm open(): ofstream File; File.open(“finename.txt”); Hàm open() nguyên dạng: void open(char *filename, [int open_mode]); trong đó tham số open_mode chỉ ra phương thức mở file. Nó có thể có các giá trị sau: + ios::in -> mở file để đọc. + ios::out -> mở file để ghi. + ios::app -> mở file để ghi vào cuối file. Nó gọi ios::out (?). + ios::ate -> mở file để ghi vào cuối. Nó không gọi ios::out (?). + ios::trunc -> xoá tất cả nội dung đã có. + ios::nocreate -> nếu file chưa tồn tại, sẽ không mở được. + ios::noreplace -> nếu file đã tồn tại, cố gắng mở sẽ gây lỗi. + ios::binary -> mở file binary. Ví dụ 1: #include <fstream.h> void main() { ofstream WriteToFile(“first.txt”, ios::ate); WriteToFile<<”\nI love you”; WriteToFile.close(); } Kết quả là file “first.txt” (phần trước) có nội dung là: Hello World! I love you Nếu không có tham số ios::ate thì nội dung của file lúc trước sẽ bị xóa. Ví dụ 2: Nếu chưa có file “non_exit.txt”. #include <fstream.h> void main() { ofstream File(“non_exit.txt”, ios::nocreate); if(!File) { cout<<”Error openning.”; exit(1); } File.close(); } Kết quả: “Error openning.” và không tạo file trong thư mục của bạn. Nếu không có tham số ios::nocreate thì vẫn vô tư, nghĩa là không có dòng thông báo lỗi và vẫn tạo ra file “non_exit.txt”. Ví dụ 3: #include<fstream.h> int main() { ifstream File(“first.txt”, ios::noreplace); if(!File) { cout<<”Error!”; exit(1); } File.close(); return 0; } Kết quả: “Error!”, vì file “first.txt” đã tồn tại. Tham số ios::noreplace không cho phép mở file đã tồn tại. Nếu bạn muốn có nhiều phương thức mở bạn dùng toán tử OR (|): ios::ate | ios::binary Sử dụng open_mode, chúng ta có thể vừa mở file để đọc và để ghi. Ví dụ: #include <fstream.h> void main() { fstream File("second.txt",ios::in | ios::out); File << "Hi! I’m there."; char ch; File.seekg(ios::beg); while(!File.eof()) { File.get(ch); cout <<ch; } cout<<endl; File.close(); } Có cái gì mới mẻ ở đây ấy nhỉ. Okay, chúng ta sẽ hiểu ngay thôi. fstream File(“test.txt”, ios::in | ios::out); - vì mở file vừa để đọc và ghi nên chúng ta tạo một đối tượng thuộc lớp fstream (chứ không phải ofstream-để ghi hay ifstream để đọc). File.seekg (ios::beg); - A đây rồi, cái này có vẻ hơi mới mẻ nhỉ (tất nhiên là với người bắt đầu học). Để xem tại sao lại phải sử dụng “nó” chúng ta hãy xem khối lệnh: while(!OpenFile.eof()) { OpenFile.get(ch); cout << ch; } Khối lệnh này chắc hẳn bạn đã biết. Hàm get(ch) sẽ đọc một kí tự của file và gán cho biến ch. Vòng lặp sẽ kết thúc khi đến cuối file. Để vòng lặp “biết” được khi nào thì kết thúc file thì, khi đọc file có một “con trỏ trong” (inside-pointer). Sau khi get(ch) thực hiện việc gán cho ch kí tự hiện hành thì “inside-pointer” sẽ chỉ đến kí tự tiếp theo. Cứ thế, đến lần lặp sau ch sẽ nhận được giá trị tiếp theo cho đến khi kết thúc file. Quay trở lại hàm seekg(). Lý do phải sử dụng hàm này vì sau câu lệnh: File<<”Hi! I’m there”; thì “inside-pointer” sẽ trỏ đến cuối file. Vì vậy phải dùng câu lệnh: File.seekg(ios::beg); để đưa “inside-pointer” về đầu file. Bạn có thể sử dụng các tham số cho hàm seekg() như sau: ios::beg – đưa “inside-pointer” về đầu file (có giá trị là 0). ios::end - đưa “inside-pointer” về cuối file. Nhưng tại sao tôi thấy tham số này luôn chỉ đến kí tự thứ 3 thế nhỉ (giá trị 2)? Hoặc bạn có thể dùng: File.seekg(4); -> “inside-pointer” chỉ đến kí tự thứ 4 kể từ đầu file (ký tự đầu là 0). File.seekg(-5,ios::end); -> đưa “inside-pointer” đến cuối file sau đó quay lại 5 kí tự. Ví dụ: #include<fstream.h> main() { ifstream File(“second.txt”); char ch; File.seekg(-5, ios:end); File.get(ch); cout<<ch; File.close(); } Nội dung file “second.txt” là “Hi! I’m there.” Kết quả được “h”. Như trên đã nói, ngoài cách đọc từng char, ta có thể đọc từng word, hay từng line. Ví dụ: #include<fstream.h> void main { ifstream File(“first.txt”, ios::in); char str[30];//lưu trữ từng từ vào str. while(!File.eof()) { File >> str; cout << str<<” “; } File.close(); } File>>str sẽ đọc từng từ và gán cho str. Kết quả ta sẽ được “Hello World! I love you.” (không xuống dòng-đây là nhược điểm của phương pháp này). Bạn có thể đọc từng line. Ví dụ: #include<fstream.h> void main() { ifstream File(“first.txt”); char line[100]; //lưu trữ toàn bộ dòng vào biến này while(!File.eof()) { File.getline(line,100); cout << line << endl; } File.close(); } Kết quả: Hello World! I love you. Như vậy, một điều khuyên cho các bạn đó là sử dụng cách đọc từng char, hoặc từng line. Vậy ta có thể đọc file theo 3 cách: - Đọc từng char: File.get(ch); - Đọc từng word: File>>ch; - Đọc từng line: File.getline(ch,dimension); Bây giờ chúng ta sẽ "check" xem file có mở được hay không: Cách 1: ofstream (ifstream) File("somefile.txt"); if(!File) { } Cách này đã được sử dụng ở một số ví dụ trước. Cách 2: Sử dụng hàm fail(): ofstream File("somefile.txt"); if(File.fail()) { } Hàm fail() trả về giá trị 0 nếu có lỗi (không mở được file) hay 1 nếu không có lỗi. Để kiểm tra xem file đã được mở chưa ta dùng hàm is_open(). Hàm trả về 0 nếu file chưa được mở, 1 nếu file đã được mở. Ví dụ: ofstream File; File.open("file1.txt"); cout << File1.is_open(); sẽ cho giá trị 1. 4. Kiểm tra cờ trạng thái I/O (I/O status-flag). Hệ thống I/O trong C++ nắm giữ các thông tin về kết quả của các toán tử I/O. Trạng thái hiện hành được lưu trữ trong một đối tượng kiểu io_state (tương tự open_mode) với những giá trị sau: + ios::goodbit: không có lỗi. + ios::eofbit: tới cuối file. + ios::failbit: lỗi không gây hại (non-fatal). + ios::badbit: lỗi gây hại (fatal). Có hai cách để nhận được thông tin về trạng thái I/O. Cách thứ nhất là gọi hàm rdstate(). Hàm này trả về trạng thái của cờ lỗi. Ví dụ, hàm rdstate() sẽ trả về giá trị ios::goobit nếu không có lỗi Cách thứ hai là sử dụng một trong các hàm sau: bool bad() -> 1 nếu có lỗi badbit. bool eof() -> 1 nếu đến cuối file. bool fail() ->1 nếu có lỗi failbit. bool good() ->1 nếu không có lỗi. Ví dụ: ofstream File("somefile.txt"); if(File.bad()==1) { } Khi một lỗi xảy ra, bạn có thể xoá bỏ lỗi đó (thiết lập lại cờ trạng thái). Để làm việc này bạn dùng hàm clear(arg), với arg là cờ trạng thái mà bạn muốn thiết lập khi có lỗi. Các ví dụ về cờ trạng thái: Ví dụ 1: Sử dụng hàm rdstate(). ifstream File("somefile.txt"); if(File.rdstate() == ios::eofbit) cout << "End of file!\n"; if(File.rdstate() == ios::badbit) cout << "Fatal I/O error!\n"; if(File.rdstate() == ios::failbit) cout << "Non-fatal I/O error!\n"; if(File.rdstate() == ios::goodbit) cout << "No errors!\n"; Ví dụ 2: Sử dụng hàm clear(). #include <fstream.h> void main() { ofstream File("love.txt"); //tạo file love.txt File.close(); //Việc mở file dưới đây sẽ gây lỗi vì sử dụng open_mode là ios::noreplace ofstream Test("love.txt",ios::noreplace); //Lỗi này là lỗi ios::failbit. if(Test.rdstate() == ios::failbit) cout << "Error !\n"; Test.clear(ios::goodbit); //Đặt flag sang trạng thái goodbit. if(Test.rdstate() == ios::goodbit) cout << "Fine!\n"; Test.clear(ios::eofbit); //Đặt sang trạng thái eofbit. if(Test.rdstate() == ios::eofbit) cout << "EOF!\n"; Test.close(); } 5. Xử lý file nhị phân Mặc dù các file với định dạng text rất hữu dụng, nhưng đôi khi bạn cần làm việc với các file nhị phân. Các hàm để ghi/đọc với các file này là get() và put(). Để đọc một byte, sử dụng hàm get() và để ghi một byte sử dụng hàm put(). Cả hai hàm này đều nhận một đối số. Lưu ý là các hàm này chỉ ghi/đọc được một byte, nghĩa là một ký tự. Nếu bạn muốn đọc/ghi một khối dữ liệu, bạn có thể sử dụng các hàm read() và write(). Nguyên mẫu của chúng là: istream &read(char *buf, streamsize num); ostream &write(const char *buf, streamsize num); Với hàm read(), buf nên là một mảng ký tự mà khối dữ liệu đọc sẽ được đặt vào. Với hàm write(), buf là một mảng ký tự,là dữ liệu mà bạn muốn ghi vào file. Với cả hai hàm num là một số xác định dữ liệu được đọc/ghi. Nếu bạn đi đến cuối file, trước khi bạn đọc "num" ký tự, bạn có thể thấy bao nhiêu ký tự đọc được bằng cách dùng hàm gcount(). Ví dụ 1: Sử dụng hàm put() và get(). #include <fstream.h> void main() { fstream File("iloveyou.txt",ios::out | ios::in | ios::binary); char ch; ch='o'; File.put(ch); //Đưa nội dung của ch vào file. File.seekg(ios::beg); //đến đầu file. File.get(ch); //đọc một ký tự cout << ch << endl; File.close(); } Ví dụ 2: Sử dụng read() and write() #include <fstream.h> #include <string.h> void main() { fstream File("test_file.txt",ios::out | ios::in | ios::binary); char arr[13]; strcpy(arr,"Hello World!"); //copy "Hello World!" vào arr File.write(arr,5); //ghi 5 ký tự đầu vào file- "Hello" File.seekg(ios::beg); //trở về đầu file static char read_array[10]; File.read(read_array,3); //Đọc 3 ký tự - "Hel" cout << read_array << endl; File.close(); } 6. Một số hàm hữu dụng. + tellg() – Trả về một giá trị nguyên, cho biết vị trí hiện tại của inside-pointer. Hàm này dùng khi đọc file. Ví dụ: #include <fstream.h> void main() { [...]... sẽ dịch chuyển inside-pointer về trước một ký tự Ví dụ: #include void main() { ifstream File( "test _file. txt"); char ch; File. get(ch); cout . “inside-pointer” về đầu file. Bạn có thể sử dụng các tham số cho hàm seekg() như sau: ios::beg – đưa “inside-pointer” về đầu file (có giá trị là 0). ios::end - đưa “inside-pointer” về cuối file. . đọc từng line. Ví dụ: #include<fstream.h> void main() { ifstream File( “first.txt”); char line[100]; //lưu trữ toàn bộ dòng vào biến này while( !File. eof()) { File. getline(line,100);. bạn có thể dùng: File. seekg(4); -> “inside-pointer” chỉ đến kí tự thứ 4 kể từ đầu file (ký tự đầu là 0). File. seekg(-5,ios::end); -> đưa “inside-pointer” đến cuối file sau đó quay lại