中國大學MOOC程序設計與算法(三):C++ 面向對象程序設計 第七週 輸入輸出和模板 筆記 之 文件讀寫(二)

第七週 輸入輸出和模板
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

發佈了53 篇原創文章 · 獲贊 4 · 訪問量 2006
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章