第七週 輸入輸出和模板
1.輸入輸出流相關的類
2.用流操縱算子控制輸出格式
3.文件讀寫(一)
4.文件讀寫(二)
5.函數模板
6.類模板
7.類模板與派生、友元和靜態成員變量
4.文件讀寫(二)
二進制文件讀寫
二進制讀文件:
ifstream 和 fstream的成員函數:istream& read (char* s, long n);
將文件讀指針指向的地方的n個字節內容,讀入到內存地址s,然後將文件讀指針向後移動n字節 (以ios::in方式打開文件時,文件讀指針開始指向文件開頭) 。
二進制寫文件:
ofstream 和 fstream的成員函數:istream& write (const char* s, long n);
將內存地址s處的n個字節內容,寫入到文件中寫指針指向的位置,然後將文件寫指針向後移動n字節(以ios::out方式打開文件時,文件寫指針開始指向文件開頭, 以ios::app方式打開文件時,文件寫指針開始指向文件尾部 ) 。
例子:在文件中寫入和讀取一個整數
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream fout("some.dat", ios::out | ios::binary);
int x=120;
fout.write( (const char *)(&x), sizeof(int) );
fout.close();
ifstream fin("some.dat",ios::in | ios::binary);
int y;
fin.read((char *) (&y),sizeof(int));
fin.close();
cout << y <<endl;
return 0;
}
例子:從鍵盤輸入幾個學生的姓名的成績,並以二進制文件形式保存
#include <iostream>
#include <fstream>
using namespace std;
struct Student {
char name[20];//20個字節
int score;//4個字節
};
int main() {
Student s;
ofstream OutFile( "c:\\tmp\\students.dat",ios::out|ios::binary);
while( cin >> s.name >> s.score )
OutFile.write( (char * ) & s, sizeof( s) );
OutFile.close();
return 0;
}
輸入:
Tom 60↙
Jack 80↙
Jane 40↙
^Z↙
則形成的 students.dat 爲 72(每個學生24字節)字節,用記事本打開,呈現亂碼:Tom 燙燙燙燙燙燙燙燙< Jack 燙燙燙燙燙燙燙蘌 Jane 燙燙燙燙燙燙燙?
亂碼的原因:Tom Jack Jane是ASCII碼寫入的,所以記事本打開可以識別,而60 80 40是以這個整數的二進制形式,而不是字符形式的ASCII碼寫入,所以記事本無法正確讀它們。
例子:將 students.dat 文件的內容讀出並顯示
#include <iostream>
#include <fstream>
using namespace std;
struct Student {
char name[20];
int score;
};
int main() {
Student s;
ifstream inFile("students.dat",ios::in | ios::binary );
if(!inFile) {
cout << "error" <<endl;
return 0;
}
while( inFile.read( (char* ) (&s), sizeof(s) ) ) {
int readedBytes = inFile.gcount(); //看剛纔讀了多少字節
cout << s.name << " " << s.score << endl;
}
inFile.close();
return 0;
}
輸出:
Tom 60
Jack 80
Jane 40
例子:將students.dat 文件的Jane的名字改成Mike
#include <iostream>
#include <fstream>
using namespace std;
struct Student {
char name[20];
int score;
};
int main()
{
Student s;
fstream iofile( "c:\\tmp\\students.dat", ios::in|ios::out|ios::binary);
if( !iofile) {
cout << "error" ;
return 0;
}
iofile.seekp( 2 * sizeof(s),ios::beg); //定位寫指針到第三個記錄
iofile.write("Mike",strlen("Mike")+1);//+1是要寫入'\0'
iofile.seekg(0,ios::beg); //定位讀指針到開頭
while( iofile.read( (char* ) & s, sizeof(s)) )
cout << s.name << " " << s.score << endl;
iofile.close();
return 0;
}
輸出:
Tom 60
Jack 80
Mike 40
例子:文件拷貝程序mycopy 示例
/*用法示例,在命令行中運行:mycopy src.dat dest.dat 即將 src.dat 拷貝到 dest.dat 如果 dest.dat 原來就有,則原來的文件會被覆蓋 */
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char * argv[])
{
if( argc != 3 ) {
cout << "File name missing!" << endl;
return 0;
}
ifstream inFile(argv[1],ios::binary|ios::in); //打開文件用於讀
if( ! inFile ) {
cout << "Source file open error." << endl;
return 0;
}
ofstream outFile(argv[2],ios::binary|ios::out); //打開文件用於寫
if( !outFile) {
cout << "New file open error." << endl;
inFile.close(); //打開的文件一定要關閉
return 0;
}
char c;
while( inFile.get(c)) //每次讀取一個字符,get每次讀一個字節
outFile.put(c); //每次寫入一個字符,put每次寫入一個字節
outFile.close();
inFile.close();
return 0;
}
內存緩衝區的作用:inFile.get每次讀一個字節,但實際是將它所在的扇區(4kb or 8kb)都讀到內存裏,下次get直接就在內存裏讀了,這樣就很快,避免了頻繁訪問硬盤。
二進制文件和文本文件的區別
標明ios::binary就是以二進制打開,不寫就是以文本方式打開。
Linux,Unix下的換行符號:’\n’(ASCII碼: 0x0a)
Windows 下的換行符號:’\r\n’ (ASCII碼:0x0d0a),endl 就是 ‘\n’
Mac OS下的換行符號: ‘\r’ (ASCII碼:0x0d)
導致 Linux, Mac OS中的文本文件在Windows 記事本中打開時不換行
Unix/Linux/Mac OS下打開文件,用不用 ios::binary 沒區別
Windows下打開文件,如果不用 ios::binary,則:
(1)讀取文件時,所有的 ‘\r\n’會被當做一個字符’\n’處理,即少讀了一個字符’\r’。
(2)寫入文件時,寫入單獨的’\n’時,系統自動在前面加一個’\r’,即多寫了一個’\r’。
所以,在windows上讀寫文件時,如果這個文件類型不可以直接用記事本打開的,一定要用 ios::binary。