C++學習筆記之輸入、輸出和文件
一、流的概念
數據從內存的一個地址移動到另一個地址稱為數據流動——流操作
流操作是通過緩沖區(buffer)機制實現的。
緩沖區:內存的一塊區域——用作文件與內存交換數據。
數據從文件中讀出:文件 → 緩沖區 → 內存
將數據寫入文件:內存 → 緩沖區 → 文件
為什麼要使用緩沖區而不直接從文件中讀取數據到內存或者直接有內存寫入文件呢?我們的文件通常都存在磁盤中,程序從磁盤讀取一個字符需要大量的硬件活動,速度非常慢。緩沖方法則從磁盤上讀取大量信息,將這些信息存儲在緩沖區,然後每次從緩沖區裡讀取一個字節,因為從內存中讀取單個字節的速度比從硬盤上讀取快很多,所以這種方法更快,也更方便。說了這麼多,只需知道緩沖方法更高效就可以了。
二、流庫(stream library)介紹
使用繼承的方法建立的輸入輸出類庫,包含兩個平行的基本類:streambuf和ios類,所有流類均以兩者之一作為基類。
streambuf類提供對緩沖區的低級操作:設置緩沖區,對緩沖區指針操作,向緩沖區存取字符等
ios類及其派生類提供用戶使用流類的接口
本文主要介紹ios,因為ios是編程中幾乎必用的類,而streambuf類很少直接使用。下面圖片來自網絡:
三、文件打開與關閉
當程序中進行文件操作時,應加上頭文件“fstream”
若要打開文件進行相應的操作,必須定義相應的流對象。如:ifstream in; // 文件輸入流對象,ofstream out; // 文件輸出流對象, fstream both; // 文件輸入/輸出流對象
文件打開用成員函數open();
原型 void(const char * s, ios_base::openmode, mode = ios_base::out|ios_base::trunc)
第一個參數表示打開文件的路徑,第二個參數表示文件打開方式,第三個參數表示訪問方式,
標志 含義
ios::ate 打開文件並移動到文件尾
ios::app 追加到文件尾
ios:in 作為輸入文件(ifstream默認)
ios::out 作為輸出文件(ofstream默認)
ios::trunc 若文件存在,清除文件內容(默認)
ios::nocreate 若文件不存在,返回錯誤
ios::noreplace 若文件存在,返回錯誤
ios::binary 以二進制方式打開
文件打開的方法:
方法一:先定義一個文件流對象,再用文件流對象調用成員函數open( )打開一個文件。如:
1 ifstream f1; //定義文件輸入流對象f1
2 f1.open( “d:\\vcprg\\7-3.cpp”); //打開D盤vcprg文件夾(目錄)下的7-3.cpp文件,可進行文件讀操作
方法二:在定義文件流對象時利用構造函數打開文件。如:
ifstream f1( “d:\\vcprg\\7-3.cpp”)
用文件流對象打開文件後,該對象就代表了被打開的文件。
復制代碼
1 #include<iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 void main(void)
7 {
8 ifstream f1; //定義文件輸入流對象
9 f1.open("d:\\vcprg\\7-3.cpp"); //打開文件
10 char c;
11 while(!f1.eof()) //判斷文件未結束則循環,eof()在文件結束時返回true
12 {
13 f1.get(c);
14 cout << c;
15 }
16 cout << endl;
17 f1.close(); //關閉文件
18 }
復制代碼
文件訪問方式:
當用ifstream定義流對象並打開一個文件時,默認為ios_base::in方式;
當用ofstream定義流對象並打開一個文件時,默認為ios_base::out方式;
當用fstream定義流對象並打開一個文件時,應給出打開方式(可用“位或”運算以多種方式打開文件)如:
1 fstream f;
2 f.open(“file.cpp”,ios_base::in|ios_base::out)
例:將D盤vcprg文件夾(目錄)下的7-3.cpp文件,復制為text.txt文件。
復制代碼
1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 void main(void)
7 {
8 ifstream f1; //定義文件輸入流對象
9 ofstream f2;
10 f1.open("d:\\vcprg\\7-3.cpp"); //以讀方式打開文件
11 f2.open("d:\\vcprg\\text.txt"); //以寫方式打開文件
12 char c;
13 while(!f1.eof()) //判斷文件未結束則循環,eof()會在文件結束時返回true
14 {
15 f1.get(c);
16 f2<<c;
17 }
18 cout << "文件復制成功" << endl;
19 f1.close(); //關閉文件
20 f2.close();
21 }
復制代碼
文件異常處理:
當打開一個文件可能失敗,在程序中通常應加上異常處理程序。
(1) 用條件語句判斷文件流標志位failbit是否為true。
if(!文件流對象){<異常處理程序>}
if(文件流對象){<正常處理程序>}(上面一段示例的13行就是用這種方式)
(2)用成員fail()函數
if(文件流對象.fail()){<異常處理程序>}
if(!文件流對象.fail()){<正常處理程序>}
(3)用成員函數good()
if(!文件流對象.good()){<異常處理程序>}
if(文件流對象.good()){<正常處理程序>}
(4)較新的C++實現提供了一種更好的方式is_open()方法,推薦用此方法,但是老式C++沒有實現這種方法,用上面三種均可
文件的關閉:
格式:文件流對象.close();
關閉文件操作包括把緩沖區數據完整地寫入文件,添加文件結束標志,切斷流對象和外部文件的連接
若流對象的生存期沒有結束,可以重用
當一個流對象的生存期結束,系統也會自動關閉文件
例如:
復制代碼
1 ofstream ofile ; // 創建輸出文件流
2
3 ofile . open ( "myfile1" ) ; // ofile流與文件“myfile1”相關聯
4
5 …… // 訪問文件“myfile1”
6
7 ofile . close ( ) ; // 關閉文件“myfile1”
8
9 ofile . open ( "myfile2" ) ; // 重用ofile流
復制代碼
四、文件讀寫
文本文件的讀寫:
方法get(char&)和get()提供不跳過空白的單字符輸入功能;
方法put(char&)和put()提供不跳過空白的單字符輸出功能;
函數get(char*,int,char)和getline(char*,int,char)在默認情況下讀取整行;
二進制文件的讀寫:
二進制文件的讀/寫分別用成員函數read( )、write( )實現。
寫二制文件的格式,輸出文件流對象.write((char*)&對象或&對象數組名[下標],sizeof(對象名或所屬類名));
讀二進制文件的格式,輸入文件流對象.read((char*)&對象或&對象數組名[下標],sizeof(對象名或所屬類名));
使用格式控制(關於格式控制可以參考http://wonderow.cnblogs.com/archive/2005/06/21/178719.html,寫得很詳細)建立的文本文件:
復制代碼
1 #include <fstream>
2 #include <iomanip>
3
4 using namespace std;
5
6
7 int main ()
8 {
9 ofstream ost ;
10 ost.open ( "d:\\my2.dat" ) ;
11 ost << "1234567890" << endl ;
12 int a = 123 ;
13 ost << a << endl ;
14 ost << setw ( 10 ) << a << endl ;
15 ost << resetiosflags ( ios :: right ) << setiosflags ( ios :: left )
16 << setfill ( '#' ) << setw ( 10 ) << a << endl ;
17 ost << resetiosflags ( ios :: left ) << setiosflags ( ios :: right )
18 << setprecision ( 5 ) << setw ( 10 ) << 12.34567890 << endl ;
19 ost . close ( ) ;
20 return 0;
21 }
復制代碼
建立一個包含學生學號、姓名、成績的文本文件:
復制代碼
1 #include <iostream>
2 #include <fstream>
3 #include <cstdlib>
4
5 using namespace std;
6
7 int main()
8 {
9 char fileName[30] , name[30] ;
10 int number , score ;
11
12 ofstream outstuf; //建立輸出流對象
13
14 cout << "Please input the name of students file :\n" ;
15 cin >> fileName ; //輸入文件名
16
17 outstuf.open(fileName, ios::out) ; //以輸出方式打開文件
18
19 if ( !outstuf )
20 {
21 cerr << "File could not be open." << endl ;
22 exit(1);
23 }
24 outstuf << "學生成績文件\n" ;
25 cout << "Input the number, name, and score : (Enter Ctrl-Z to end input)\n? " ;//windows中Ctrl-Z 結束輸入和輸出
26
27 while( cin >> number >> name >> score )
28 {
29 outstuf << number << ' ' << name << ' ' << score << '\n' ;
30 cout << "? " ;
31 }
32 outstuf.close() ;
33 return 0;
34 }
復制代碼
二進制文件,從一個學校教程裡搜羅的圖
五、總結
istream類定義了多個版本的抽取運算符(>>),用於識別所有基本的C++類型,並將字符輸入轉換為這些類型。get()方法族和getline()方法為單字符輸入和字符串輸入提供了進一步支持;
ostream類定義了多個版本的插入運算符(>>),用於識別所有基本的C++類型,並將字符輸入轉換為這些類型輸出。put()方法為單字符輸入和字符串輸入提供了進一步支持;
使用ios_base類方法以及文件iostream和iomanip中定義的控制符(參考http://wonderow.cnblogs.com/archive/2005/06/21/178719.html)可以控制格式化輸出;
對文件操作,必須將文件與流關聯起來,通過ifstream對象與文件關聯,可以使用所有istream方法來讀取文件,使用ofstream對象與文件關聯起來,可以使用ofstream方法來寫文件,使用fstream對象關聯文件,可以將fstream的輸入和輸出方法用於文件;
要將文件與流關聯,先創建一個文件流對象,然後用open()方法將這個流與文件關聯,close()方法終止流與文件的關聯;
文本文件是以字符格式存儲信息,例如數字值將會被轉變為字符表示;
seekg()和seekp()通過指針提供對文件的隨機存取。tellg()和tellp()方法報告當前文件的位置;