1. 程式人生 > >C++學習筆記——輸入與輸出

C++學習筆記——輸入與輸出

概念

C++輸入輸出包含以下三個方面的內容:

  1. 對系統指定的標準裝置的輸入和輸出。即從鍵盤輸入資料,輸出到顯示器螢幕。這種輸入輸出稱為標準的輸入輸出,簡稱標準I/O。
  2. 以外存磁碟檔案為物件進行輸入和輸出,即從磁碟檔案輸入資料,資料輸出到磁碟檔案。以外存檔案為物件的輸入輸出稱為檔案的輸入輸出,簡稱檔案I/O。
  3. 對記憶體中指定的空間進行輸入和輸出。通常指定一個字元陣列作為儲存空間(實際上可以利用該空間儲存任何資訊)。這種輸入和輸出稱為字串輸入輸出,簡稱串I/O。

與C++輸入輸出有關的類和物件

I/O類庫中的常用流類
類名 作用 在哪個標頭檔案中宣告
ios 抽象基類 iostream
istream
ostream
iostream
通用輸入流和其他輸入流的基類
通用輸出流和其他輸出流的基類
通用輸入輸出流和其他輸入輸出流的基類
iostream
iostream
iostream
ifstream
ofstream
fstream
輸入檔案流類
輸出檔案流類
輸入輸出檔案流類
fstream
fstream
fstream
istrstream
ostrstream
strstream
輸入字串流類
輸出字串流類
輸入輸出字串流類
strstream
strstream
strstream

與iostream類庫有關的標頭檔案

iostream類庫中不同的類的宣告被放在不同的標頭檔案中,使用者在自己的程式中用#include命令包含了有關的標頭檔案就相當於在本程式中聲明瞭所需要用到的類。可以換 —種說法:標頭檔案是程式與類庫的介面,iostream類庫的介面分別由不同的標頭檔案來實現。常用的有

  • iostream 包含了對輸入輸出流進行操作所需的基本資訊。
  • fstream 用於使用者管理的檔案的I/O操作。
  • strstream 用於字串流I/O。
  • stdiostream 用於混合使用C和C + +的I/O機制時,例如想將C程式轉變為C++程式。
  • iomanip 在使用格式化I/O時應包含此標頭檔案。

檔案中定義的4種流物件

物件 含義 對應裝置 對應的類 c語言中相應的標準檔案
cin 標準輸入流 鍵盤 istream_withassign stdin
cout 標準輸出流 螢幕 ostream_withassign stderr
cerr 標準錯誤流 螢幕 ostream_withassign stderr
clog 標準錯誤流 螢幕 ostream_withassign stderr

在iostream標頭檔案中過載運算子

“<<”和“>>”本來在C++中是被定義為左位移運算子和右位移運算子的,由於在iostream標頭檔案中對它們進行了過載,使它們能用作標準型別資料的輸入和輸出運算子。所以,在用它們的程式中必須用#include命令把iostream包含到程式中。

    #include <iostream>

在istream和ostream類(這兩個類都是在iostream中宣告的)中分別有一組成員函式對位移運算子“<<”和“>>”進行過載,以便能用它輸入或輸出各種標準資料型別的資料。對於不同的標準資料型別要分別進行過載,如:

    ostream operator << (int );  //用於向輸出流插入一個int資料
    ostream operator << (float );  //用於向輸出流插入一個float資料
    ostream operator << (char);  //用於向輸出流插入一個char資料
    ostream operator << (char * );  //用於向輸出流插入一個字串資料

等。如果在程式中有下面的表示式:

 cout<<"C++";

實際上相當於:

 cout.operator <<("C++")

標準輸出流

cout流物件

1) cout不是C++預定義的關鍵字,它是ostream流類的物件,在iostream中定義。
2)用“cout<<”輸出基本型別的資料時,可以不必考慮資料是什麼型別,系統會判斷資料的型別,並根據其型別選擇呼叫與之匹配的運算子過載函式。
3) cout流在記憶體中對應開闢了一個緩衝區,用來存放流中的資料,當向cout流插 人一個endl時,不論緩衝區是否已滿,都立即輸出流中所有資料,然後插入一個換行符, 並重新整理流(清空緩衝區)。
4) 在iostream中只對”<<”和”>>”運算子用於標準型別資料的輸入輸出進行了過載,但未對使用者宣告的型別資料的輸入輸出進行過載。

cerr流物件

cerr流物件是標準錯誤流,cerr流已被指定為與顯示器關聯。cerr的作用是向標準錯誤裝置(standard error device)輸出有關出錯資訊。cerr與標準輸出流cout的作用和用法差不多。但有一點不同:cout流通常是傳送到顯示器輸出,但也可以被重定向輸出到磁碟檔案,而cerr流中的資訊只能在顯示器輸出。

clog流物件

clog流物件也是標準錯誤流,它是console log的縮寫。它的作用和cerr相同,都是在終端顯示器上顯示出錯資訊。區別:cerr是不經過緩衝區,直接向顯示器上輸出有關資訊,而clog中的資訊存放在緩衝區中,緩衝區滿後或遇endl時向顯示器輸出。

格式化輸出

#include <iostream>
#include <iomanip>//不要忘記包含此標頭檔案
using namespace std;
int main()
{
   int a;
   cout<<"input a:";
   cin>>a;
   cout<<"dec:"<<dec<<a<<endl;  //以十進位制形式輸出整數
   cout<<"hex:"<<hex<<a<<endl;  //以十六進位制形式輸出整數a
   cout<<"oct:"<<setbase(8)<<a<<endl;  //以八進位制形式輸出整數a
   char *pt="China";  //pt指向字串"China"
   cout<<setw(10)<<pt<<endl;  //指定域寬為,輸出字串
   cout<<setfill('*')<<setw(10)<<pt<<endl;  //指定域寬,輸出字串,空白處以'*'填充
   double pi=22.0/7.0;  //計算pi值
   //按指數形式輸出,8位小數
   cout<<setiosflags(ios::scientific)<<setprecision(8);
   cout<<"pi="<<pi<<endl;  //輸出pi值
   cout<<"pi="<<setprecision(4)<<pi<<endl;  //改為位小數
   cout<<"pi="<<setiosflags(ios::fixed)<<pi<<endl;  //改為小數形式輸出
   return 0;
}

用於控輸出格式的流成員函式

流成員函式 與之作用相同的控制符 作用
precision(n) setprecision(n) 設定實數的精度為n位
width(n) setw(n) 設定欄位寬度為n位
fill(c) setfill(c) 設定填充字元c
setf() setiosflags() 設定輸出格式狀態,括號中應給出格式狀態,內容與控制符setiosflags括號中的內容相同,如表13.5所示
unsetf() resetioflags() 終止已設定的輸出格式狀態,在括號中應指定內容

表13.5 設定格式狀態的格式標誌

格式標誌 作用
ios::left 輸出資料在本域寬範圍內向左對齊
ios::right 輸出資料在本域寬範圍內向右對齊
ios::internal 數值的符號位在域寬內左對齊,數值右對齊,中間由填充字元填充
ios::dec 設定整數的基數為10
ios::oct 設定整數的基數為8
ios::hex 設定整數的基數為16
ios::showbase 強制輸出整數的基數(八進位制數以0打頭,十六進位制數以0x打頭)
ios::showpoint 強制輸出浮點數的小點和尾數0
ios::uppercase 在以科學記數法格式E和以十六進位制輸出字母時以大寫表示
ios::showpos 對正數顯示“+”號
ios::scientific 浮點數以科學記數法格式輸出
ios::fixed 浮點數以定點格式(小數形式)輸出
ios::unitbuf 每次輸出之後重新整理所有的流
ios::stdio 每次輸出之後清除stdout, stderr
#include <iostream>
using namespace std;
int main( )
{
   int a=21
   cout.setf(ios::showbase);//顯示基數符號(0x或)
   cout<<"dec:"<<a<<endl;         //預設以十進位制形式輸出a
   cout.unsetf(ios::dec);         //終止十進位制的格式設定
   cout.setf(ios::hex);           //設定以十六進位制輸出的狀態
   cout<<"hex:"<<a<<endl;         //以十六進位制形式輸出a
   cout.unsetf(ios::hex);         //終止十六進位制的格式設定
   cout.setf(ios::oct);           //設定以八進位制輸出的狀態
   cout<<"oct:"<<a<<endl;         //以八進位制形式輸出a
   cout.unseft(ios::oct);
   char *pt="China";              //pt指向字串"China"
   cout.width(10);                //指定域寬為
   cout<<pt<<endl;                //輸出字串
   cout.width(10);                //指定域寬為
   cout.fill('*');                //指定空白處以'*'填充
   cout<<pt<<endl;                //輸出字串
   double pi=22.0/7.0;            //輸出pi值
   cout.setf(ios::scientific);    //指定用科學記數法輸出
   cout<<"pi=";                   //輸出"pi="
   cout.width(14);                //指定域寬為
   cout<<pi<<endl;                //輸出pi值
   cout.unsetf(ios::scientific); //終止科學記數法狀態
   cout.setf(ios::fixed);        //指定用定點形式輸出
   cout.width(12);               //指定域寬為
   cout.setf(ios::showpos);      //正數輸出“+”號
   cout.setf(ios::internal);     //數符出現在左側
   cout.precision(6);            //保留位小數
   cout<<pi<<endl;               //輸出pi,注意數符“+”的位置
   return 0;
}

1) 成員函式width(n)和控制符setw(n)只對其後的第一個輸出項有效。如:

    cout. width(6);
    cout <<20 <<3.14<<endl;

在輸出第一個輸出項20時,域寬為6,因此在20前面有4個空格,在輸出3.14時,width (6)已不起作用
2) 在表13.5中的輸出格式狀態分為5組,每一組中同時只能選用一種(例如dec、hex和oct中只能選一,它們是互相排斥的)。在用成員函式setf和控制符setiosflags設定輸出格式狀態後,如果想改設定為同組的另一狀態,應當呼叫成員函式unsetf(對應於成員函式self)或resetiosflags(對應於控制符setiosflags),先終止原來設定的狀態。
3) 用setf 函式設定格式狀態時,可以包含兩個或多個格式標誌,由於這些格式標誌在ios類中被定義為列舉值,每一個格式標誌以一個二進位代表,因此可以用位或運算子“|”組合多個格式標誌。
4)對輸出格式的控制,既可以用控制符,也可以用cout流的有關成員函式,二者的作用是相同的。

用C++流成員函式put輸出單個字元

在程式中一般用cout和插入運算子“<<”實現輸出,cout流在記憶體中有相應的緩衝區。有時使用者還有特殊的輸出要求,例如只輸出一個字元。ostream類除了提供上面介紹過的用於格式控制的成員函式外,還提供了專用於輸出單個字元的成員函式put。如:

    cout.put('a');

呼叫該函式的結果是在螢幕上顯示一個字元a。put函式的引數可以是字元或字元的ASCII程式碼(也可以是一個整型表示式)。

cin輸入流

通過測試cin的真值,判斷流物件是否處於正常狀態。

#include <iostream>
using namespace std;
int main( )
{
   float grade;
   cout<<"enter grade:";
   while(cin>>grade)//能從cin流讀取資料
   {
      if(grade>=85) cout<<grade<<"GOOD!"<<endl;
      if(grade<60) cout<<grade<<"fail!"<<endl;
      cout<<"enter grade:";
   }
   cout<<"The end."<<endl;
   return 0;
}

get()函式讀入一個字元

get()函式是cin輸入流物件的成員函式,它有3種形式:無引數的,有一個引數的,有3個引數的。

1) 不帶引數的get函式

用來從指定的輸入流中提取一個字元(包括空白字元),函式的返回值就是讀入的字元。 若遇到輸入流中的檔案結束符,則函式值返回檔案結束標誌EOF(End Of File),一般以-1代表EOF,用-1而不用0或正值,是考慮到不與字元的ASCII程式碼混淆,但不同的C ++系統所用的EOF值有可能不同。

2) 有一個引數的get函式

呼叫形式為

    cin.get(ch)

其作用是從輸入流中讀取一個字元,賦給字元變數ch。如果讀取成功則函式返回true(真),如失敗(遇檔案結束符) 則函式返回false(假)。

3) 有3個引數的get函式

其呼叫形式為

    cin.get(字元陣列, 字元個數n, 終止字元)

    cin.get(字元指標, 字元個數n, 終止字元)

其作用是從輸入流中讀取n-1個字元,賦給指定的字元陣列(或字元指標指向的陣列),如果在讀取n-1個字元之前遇到指定的終止字元,則提前結束讀取。如果讀取成功則函式返回true(真),如失敗(遇檔案結束符) 則函式返回false(假)。

getline()函式讀入一行字元

getline函式的作用是從輸入流中讀取一行字元,其用法與帶3個引數的get函式類似。即

    cin.getline(字元陣列(或字元指標), 字元個數n, 終止標誌字元)
#include <iostream>
using namespace std;
int main( )
{
   char ch[20];
   cout<<"enter a sentence:"<<endl;
   cin>>ch;
   cout<<"The string read with cin is:"<<ch<<endl;
   cin.getline(ch,20,'/');  //讀個字元或遇'/'結束
   cout<<"The second part is:"<<ch<<endl;
   cin.getline(ch,20);  //讀個字元或遇'/n'結束
   cout<<"The third part is:"<<ch<<endl;
   return 0;
}

一些與輸入有關的istream類成員函式

eof 函式

eof是end of file的縮寫,表示“檔案結束”。從輸入流讀取資料,如果到達檔案末尾(遇檔案結束符),eof函式值為非零值(真),否則為0(假)。

peek函式

peek是“觀察”的意思,peek函式的作用是觀測下一個字元。其呼叫形式為:

    c=cin.peek( );

函式的返回值是指標指向的當前字元,但它只是觀測,指標仍停留在當前位置,並不後移。如果要訪問的字元是檔案結束符,則函式值是EOF(-1)。

putback函式

其呼叫形式為

 cin.putback(ch);

其作用是將前面用get或getline函式從輸入流中讀取的字元ch返回到輸入流,插入到當前指標位置,以供後面讀取。

ignore函式

其呼叫形式為

    cin.ignore(n, 終止字元)

函式作用是跳過輸入流中n個字元,或在遇到指定的終止字元時提前結束(此時跳過包括終止字元在內的若干字元)。

檔案流類與檔案流物件

檔案流是以外存檔案為輸入輸出物件的資料流。輸出檔案流是從記憶體流向外存檔案的資料,輸入檔案流是從外存檔案流向記憶體的資料。每一個檔案流都有一個記憶體緩衝區與之對應。

檔案的開啟與關閉

所謂開啟(open)檔案是一種形象的說法,如同開啟房門就可以進入房間活動一樣。 開啟檔案是指在檔案讀寫之前做必要的準備工作,包括:

  • 為檔案流物件和指定的磁碟檔案建立關聯,以便使檔案流流向指定的磁碟檔案。
  • 指定檔案的工作方式,如,該檔案是作為輸入檔案還是輸出檔案,是ASCII檔案還是二進位制檔案等。

以上工作可以通過兩種不同的方法實現。

1) 呼叫檔案流的成員函式open。如

    ofstream outfile;  //定義ofstream類(輸出檔案流類)物件outfile
    outfile.open("f1.dat",ios::out);  //使檔案流與f1.dat檔案建立關聯

2) 在定義檔案流物件時指定引數
在宣告檔案流類時定義了帶引數的建構函式,其中包含了開啟磁碟檔案的功能。因此,可以在定義檔案流物件時指定引數,呼叫檔案流類的建構函式來實現開啟檔案的功能。如

    ostream outfile("f1.dat",ios::out);

一般多用此形式,比較方便。作用與open函式相同。

檔案輸入輸出方式設定值

方式 作用
ios::in 以輸入方式開啟檔案
ios::out 以輸出方式開啟檔案(這是預設方式),如果已有此名字的檔案,則將其原有內容全部清除
ios::app 以輸出方式開啟檔案,寫入的資料新增在檔案末尾
ios::ate 開啟一個已有的檔案,檔案指標指向檔案末尾
ios: :trunc 開啟一個檔案,如果檔案已存在,則刪除其中全部資料,如檔案不存在,則建立新檔案。如已指定了 ios::out 方式,而未指定ios: :app,ios::ate,ios: :in,則同時預設此方式
ios:: binary 以二進位制方式開啟一個檔案,如不指定此方式則預設為ASCII方式
ios::nocreate 開啟一個已有的檔案,如檔案不存在,則開啟失敗。nocrcate的意思是不建立新檔案
ios:: noreplace 如果檔案不存在則建立新檔案,如果檔案已存在則操作失敗,replace 的意思是不更新原有檔案
ios::in l ios::out 以輸入和輸出方式開啟檔案,檔案可讀可寫
ios:: out l ios::binary 以二進位制方式開啟一個輸出檔案
ios::in l ios::binary 以二進位制方式開啟一個輸入檔案


在對已開啟的磁碟檔案的讀寫操作完成後,應關閉該檔案。關閉檔案用成員函式close。如

 outfile.close( );  //將輸出檔案流所關聯的磁碟檔案關閉

所謂關閉,實際上是解除該磁碟檔案與檔案流的關聯,原來設定的工作方式也失效,這樣,就不能再通過檔案流對該檔案進行輸入或輸出。此時可以將檔案流與其他磁碟檔案建立關聯,通過檔案流對新的檔案進行輸入或輸出。如

    outfile.open("f2.dat",ios::app|ios::nocreate);

此時檔案流outfile與f2.dat建立關聯,並指定了f2.dat的工作方式。

對ASCII檔案的讀寫操作

對ASCII檔案的讀寫操作可以用以下兩種方法:
1) 用流插入運算子“<<”和流提取運算子“>>”輸入輸出標準型別的資料。
2) 用檔案流的put、get、geiline等成員函式進行字元的輸入輸出

#include <fstream>
using namespace std;
int main( )
{
   int a[10];
   ofstream outfile("f1.dat",ios::out);//定義檔案流物件,開啟磁碟檔案"f1.dat"
   if(!outfile)                        //如果開啟失敗,outfile返回值
   {
      cerr<<"open error!"<<endl;
      exit(1);
   }
   cout<<"enter 10 integer numbers:"<<endl;
   for(int i=0;i<10;i++)
   {
      cin>>a[i];
      outfile<<a[i]<<" ";
   }            //向磁碟檔案"f1.dat"輸出資料
   outfile.close();                   //關閉磁碟檔案"f1.dat"
   return 0;
}

對二進位制檔案的讀寫操作

對二進位制檔案的讀寫主要用istream類的成員函式read和write來實現。這兩個成員函式的原型為

    istream& read(char *buffer,int len);
    ostream& write(const char * buffer,int len);

字元指標buffer指向記憶體中一段儲存空間。len是讀寫的位元組數。呼叫的方式為:

    a. write(p1,50);
    b. read(p2,30);

上面第一行中的a是輸出檔案流物件,write函式將字元指標p1所給出的地址開始的50個位元組的內容不加轉換地寫到磁碟檔案中。在第二行中,b是輸入檔案流物件,read 函式從b所關聯的磁碟檔案中,讀入30個位元組(或遇EOF結束),存放在字元指標p2所指的一段空間內。

寫檔案

#include <fstream>
using namespace std;
struct student
{
   char name[20];
   int num;
   int age;
   char sex;
};
int main( )
{
   student stud[3]={"Li",1001,18,'f',"Fun",1002,19,'m',"Wang",1004,17,'f'};
   ofstream outfile("stud.dat",ios::binary);
   if(!outfile)
   {
      cerr<<"open error!"<<endl;
      abort( );//退出程式
   }
   for(int i=0;i<3;i++)
      outfile.write((char*)&stud[i],sizeof(stud[i]));
   outfile.close( );
   return 0;
}

讀檔案

#include <fstream>
using namespace std;
struct student
{
   string name;
   int num;
   int age;
   char sex;
};
int main( )
{
   student stud[3];
   int i;
   ifstream infile("stud.dat",ios::binary);
   if(!infile)
   {
      cerr<<"open error!"<<endl;
      abort( );
   }
   for(i=0;i<3;i++)
   infile.read((char*)&stud[i],sizeof(stud[i]));
   infile.close( );
   for(i=0;i<3;i++)
   {
      cout<<"NO."<<i+1<<endl;
      cout<<"name:"<<stud[i].name<<endl;
      cout<<"num:"<<stud[i].num<<endl;;
      cout<<"age:"<<stud[i].age<<endl;
      cout<<"sex:"<<stud[i].sex<<endl<<endl;
   }
   return 0;
}

在磁碟檔案中有一個檔案指標,用來指明當前應進行讀寫的位置。在輸入時每讀入 一個宇節,指標就向後移動一個位元組。在輸出時每向檔案輸出一個位元組,指標就向後移動 一個位元組,隨著輸出檔案中位元組不斷增加,指標不斷後移。對於二進位制檔案,允許對指標進行控制,使它按使用者的意圖移動到所需的位置,以便在該位置上進行讀寫。檔案流提供 一些有關檔案指標的成員函式。

檔案流與檔案指標有關的成員函式
成員函式 作 用
gcount() 返回最後一次輸入所讀入的位元組數
tellg() 返回輸入檔案指標的當前位置
seekg(檔案中的位置) 將輸入檔案中指標移到指定的位置
seekg(位移量, 參照位置) 以參照位置為基礎移動若干位元組
tellp() 返回輸出檔案指標當前的位置
seekp(檔案中的位置) 將輸出檔案中指標移到指定的位置
seekp(位移量, 參照位置) 以參照位置為基礎移動若干位元組

1) 這些函式名的第一個字母或最後一個字母不是g就是p。帶 g的是用於輸入的函式(g是get的第一個字母,以g作為輸入的標識,容易理解和記憶), 帶p的是用於輸出的函式(P是put的第一個字母,以P作為輸出的標識)。

2) 函式引數中的“檔案中的位置”和“位移量”已被指定為long型整數,以位元組為單位。“參照位置”可以是下面三者之一:

 ios::beg  檔案開頭(beg是begin的縮寫),這是預設值。
    ios::cur  指標當前的位置(cur是current的縮寫)。
    ios::end  檔案末尾。

它們是在ios類中定義的列舉常量。舉例如下:

    infile.seekg(100);  //輸入檔案中的指標向前移到位元組位置
    infile.seekg(-50,ios::cur);  //輸入檔案中的指標從當前位置後移位元組
    outfile.seekp(-75,ios::end);  //輸出檔案中的指標從檔案尾後移位元組

一般情況下讀寫是順序進行的,即逐個位元組進行讀寫。但是對於二進位制資料檔案來說,可以利用上面的成員函式移動指標,隨機地訪問檔案中任一位置上的資料,還可以修改檔案中的內容。

#include <fstream>
using namespace std;
struct student
{
   int num;
   char name[20];
   float score;
};
int main( )
{
   student stud[5]={1001,"Li",85,1002,"Fun",97.5,1004,"Wang",54,1006,"Tan",76.5,1010,"ling",96};
   fstream iofile("stud.dat",ios::in|ios::out|ios::binary);
   //用fstream類定義輸入輸出二進位制檔案流物件iofile
   if(!iofile)
   {
      cerr<<"open error!"<<endl;
      abort( );
   }
   for(int i=0;i<5;i++)  //向磁碟檔案輸出個學生的資料
      iofile.write((char *)&stud[i],sizeof(stud[i]));
   student stud1[5];  //用來存放從磁碟檔案讀入的資料
   for(int i=0;i<5;i=i+2)
   {
      iofile.seekg(i*sizeof(stud[i]),ios::beg);  //定位於第,2,4學生資料開頭
      //先後讀入個學生的資料,存放在stud1[0],stud[1]和stud[2]中
      iofile.read((char *)&stud1[i/2],sizeof(stud1[0]));
      //輸出stud1[0],stud[1]和stud[2]各成員的值
      cout<<stud1[i/2].num<<" "<<stud1[i/2].name<<" "<<stud1[i/2].score<<endl;
   }
   cout<<endl;
   stud[2].num=1012;  //修改第個學生(序號為)的資料
   strcpy(stud[2].name,"Wu");
   stud[2].score=60;
   iofile.seekp(2*sizeof(stud[0]),ios::beg);  //定位於第個學生資料的開頭
   iofile.write((char *)&stud[2],sizeof(stud[2]));  //更新第個學生資料
   iofile.seekg(0,ios::beg);  //重新定位於檔案開頭
   for(int i=0;i<5;i++)
   {
      iofile.read((char *)&stud[i],sizeof(stud[i]));  //讀入個學生的資料
      cout<<stud[i].num<<" "<<stud[i].name<<" "<<stud[i].score<<endl;
   }
   iofile.close( );
   return 0;
}

請注意,不能用ifstream或ofstream類定義輸入輸出的二進位制檔案流物件,而應當用fstream類。

字串流的讀寫

檔案流是以外存檔案為輸入輸出物件的資料流,字串流不是以外存檔案為輸入輸出的物件,而以記憶體中使用者定義的字元陣列(字串)為輸入輸出的物件,即將資料輸出到記憶體中的字元陣列,或者從字元陣列(字串)將資料讀入。字串流也稱為記憶體流。

檔案流類有ifstream,ofstream和fstream,而字串流類有istrstream,ostrstream和strstream。檔案流類和字串流類都是ostream,istream和iostream類的派生類,因此對它們的操作方法是基本相同的。向記憶體中的一個字元陣列寫資料就如同向檔案寫資料一樣,但有3點不同:

  1. 輸出時資料不是流向外存檔案,而是流向記憶體中的一個儲存空間。輸入時從記憶體中的儲存空間讀取資料。在嚴格的意義上說,這不屬於輸入輸出,稱為讀寫比較合適。
    因為輸入輸出一般指的是在計算機記憶體與計算機外的檔案(外部裝置也視為檔案)之間 的資料傳送。但由於C++的字串流採用了C++的流輸入輸出機制,因此往往也用輸入和輸出來表述讀寫操作。
  2. 字串流物件關聯的不是檔案,而是記憶體中的一個字元陣列,因此不需要開啟和關閉檔案。
  3. 每個檔案的最後都有一個檔案結束符,表示檔案的結束。而字串流所關聯的字元陣列中沒有相應的結束標誌,使用者要指定一個特殊字元作為結束符,在向字元陣列寫入全部資料後要寫入此字元。

字串流類沒有open成員函式,因此要在建立字串流物件時通過給定引數來確立字串流與字元陣列的關聯。即通過呼叫建構函式來解決此問題。建立字串流物件的方法與含義如下。

建立輸出字串流物件

ostrstream類提供的建構函式的原型為:

ostrstream::ostrstream(char *buffer,int n,int mode=ios::out);

buffer是指向字元陣列首元素的指標,n為指定的流緩衝區的大小(一般選與字元陣列的大小相同,也可以不同),第3個引數是可選的,預設為ios::out方式。可以用以下語句建立輸出字串流物件並與字元陣列建立關聯:

    ostrstream strout(ch1,20);

作用是建立輸出字串流物件strout,並使strout與字元陣列ch1關聯(通過字串流將資料輸出到字元陣列ch1),流緩衝區大小為20。

建立輸入字串流物件

istrstream類提供了兩個帶參的建構函式,原型為:

    istrstream::istrstream(char *buffer);
    istrstream::istrstream(char *buffer,int n);

buffer是指向字元陣列首元素的指標,用它來初始化流物件(使流物件與字元陣列建立關聯)。可以用以下語句建立輸入字串流物件:

    istrstream strin(ch2);

作用是建立輸入字串流物件strin,將字元陣列ch2中的全部資料作為輸入字串流的內容。

    istrstream strin(ch2,20);

流緩衝區大小為20,因此只將字元陣列ch2中的,20個字元作為輸入字串流的內容。

建立輸入輸出字串流物件

strstream類提供的建構函式的原型為:

    strstream::strstream(char *buffer,int n,int mode);

可以用以下語句建立輸入輸出字串流物件:

    strstream strio(ch3,sizeof(ch3),ios::in|ios::out);

作用是建立輸入輸出字串流物件,以字元陣列ch3為輸入輸出物件,流緩衝區大小與陣列ch3相同。

以上個字串流類是在標頭檔案strstream中定義的,因此程式中在用到istrstream、ostrstream和strstream類時應包含標頭檔案strstream(在GCC中,用標頭檔案strstream)。

#include <strstream>
using namespace std;
struct student
{
   int num;
   char name[20];
   float score;
};
int main( )
{
   student stud[3]={1001,"Li",78,1002,"Wang",89.5,1004,"Fun",90};
   char c[50];  //使用者定義的字元陣列
   ostrstream strout(c,30);  //建立輸出字串流,與陣列c建立關聯,緩衝區長
   for(int i=0;i<3;i++)  //向字元陣列c寫個學生的資料
      strout<<stud[i].num<<stud[i].name<<stud[i].score;
   strout<<ends;  //ends是C++的I/O操作符,插入一個'\\0'
   cout<<"array c:"<<c<<endl;  //顯示字元陣列c中的字元
}

在一個字元陣列c中存放了個整數,以空格相間隔,要求將它們放到整型陣列中,再按大小排序,然後再存放回字元陣列c中。

#include <strstream>
using namespace std;
int main( )
{
   char c[50]="12 34 65 -23 -32 33 61 99 321 32";
   int a[10],i,j,t;
   cout<<"array c:"<<c<<endl;  //顯示字元陣列中的字串
   istrstream strin(c,sizeof(c));  //建立輸入串流物件strin並與字元陣列c關聯
   for(i=0;i<10;i++)
      strin>>a[i];  //從字元陣列c讀入個整數賦給整型陣列a
   cout<<"array a:";
   for(i=0;i<10;i++)
      cout<<a[i]<<" ";  //顯示整型陣列a各元素
   cout<<endl;
   for(i=0;i<9;i++)  //用起泡法對陣列a排序
      for(j=0;j<9-i;j++)
         if(a[j]>a[j+1])
         {t=a[j];a[j]=a[j+1];a[j+1]=t;}
   ostrstream strout(c,sizeof(c));  //建立輸出串流物件strout並與字元陣列c關聯
      for(i=0;i<10;i++)
         strout<<a[i]<<" ";  //將個整數存放在字元陣列c
   strout<<ends;  //加入'\\0'
   cout<<"array c:"<<c<<endl;  //顯示字元陣列c
   return 0;
}

對字串流的幾點說明:
1) 用字串流時不需要開啟和關閉檔案。

2) 通過字串流從字元陣列讀資料就如同從鍵盤讀資料一樣,可以從字元陣列讀入字元資料,也可以讀入整數、浮點數或其他型別資料。如果不用字串流,只能從字元陣列逐個訪問字元,而不能按其他型別的資料形式讀取資料。這是用字串流訪問字元陣列的優點,使用方便靈活。

3) 先後建立兩個字串流strin和strout,與字元陣列c關聯。strin從字元陣列c中獲取資料,strout將資料傳送給字元陣列。分別對同一字元陣列進行操作。甚至可以對字元陣列交叉進行讀寫,輸入字串流和輸出字串流分別有流指標指示當前位 置,互不干擾。

4) 用輸出字串流向字元陣列c寫資料時,是從陣列的首地址開始的,因此更新了 陣列的內容。

5) 字串流關聯的字元陣列並不一定是專為字串流而定義的陣列,它與一般的字元陣列無異,可以對該陣列進行其他各種操作。