文件流是以外存文件為輸入輸出對象的數據流,字符串流不是以外存文件為輸入輸出的對象,而以內存中用戶定義的字符數組(字符串)為輸入輸出的對象,即將數據輸出到內存中的字符數組,或者從字符數組(字符串)將數據讀入。字符串流也稱為內存流。
字符串流也有相應的緩沖區,開始時流緩沖區是空的。如果向字符數組存入數據,隨著向流插入數據,流緩沖區中的數據不斷增加,待緩沖區滿了(或遇換行符),一起存入字符數組。如果是從字符數組讀數據,先將字符數組中的數據送到流緩沖區,然後從緩沖區中提取數據賦給有關變量。
在字符數組中可以存放字符,也可以存放整數、浮點數以及其他類型的數據。在向字符數組存入數據之前,要先將數據從二進制形式轉換為ASCII代碼,然後存放在緩沖區, 再從緩沖區送到字符數組。從字符數組讀數據時,先將字符數組中的數據送到緩沖區,在賦給變量前要先將ASCII代碼轉換為二進制形式。總之,流緩沖區中的數據格式與字符數組相同。這種情況與以標准設備(鍵盤和顯示器)為對象的輸入輸出是類似的,鍵盤和顯示器都是按字符形式輸入輸出的設備,內存中的數據在輸出到顯示器之前,先要轉換為 ASCII碼形式,並送到輸出緩沖區中。從鍵盤輸入的數據以ASCII碼形式輸入到輸入緩沖區,在賦給變量前轉換為相應變量類型的二進制形式,然後賦給變量。對於字符串流的輸入輸出的情況,如不清楚,可以從對標准設備的輸入輸出中得到啟發。
文件流類有ifstream,ofstream和fstream,而字符串流類有istrstream,ostrstream和strstream。文件流類和字符串流類都是ostream,istream和iostream類的派生類,因此對它們的操作方法是基本相同的。向內存中的一個字符數組寫數據就如同向文件寫數據一樣,但有3點不同:
-
輸出時數據不是流向外存文件,而是流向內存中的一個存儲空間。輸入時從內存中的存儲空間讀取數據。在嚴格的意義上說,這不屬於輸入輸出,稱為讀寫比較合適。 因為輸入輸出一般指的是在計算機內存與計算機外的文件(外部設備也視為文件)之間 的數據傳送。但由於C++的字符串流采用了 C++的流輸入輸出機制,因此往往也用輸入和輸出來表述讀寫操作。
-
字符串流對象關聯的不是文件,而是內存中的一個字符數組,因此不需要打開和關閉文件。
-
每個文件的最後都有一個文件結束符,表示文件的結束。而字符串流所關聯的字符數組中沒有相應的結束標志,用戶要指定一個特殊字符作為結束符,在向字符數組寫入全部數據後要寫入此字符。
字符串流類沒有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)。
[例13.17] 將一組數據保存在字符數組中。
#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中的字符
}
運行時在顯示器上的輸出如下:
array c:
1001Li781002Wang89.51004Fun90
以上就是字符數組c中的字符。可以看到:
1) 字符數組c中的數據全部是以ASCII代碼形式存放的字符,而不是以二進制形式表示的數據。
2) 在建立字符串流strout時指定流緩沖區大小為30字節,與字符數組c的大小不同,這是允許的,這時字符串流最多可以傳送30個字符給字符數組c。請思考:如果將流 緩沖區大小改為10字節,即:
ostrstream.strout( c ,10);
運行情況會怎樣?流緩沖區只能存放10個字符,將這10個字符寫到字符數組c中。運行時顯示的結果是:
1001Li7810
字符數組c中只有10個有效字符。一般都把流緩沖區的大小指定與字符數組的大小 相同。
3) 字符數組c中的數據之間沒有空格,連成一片,這是由輸出的方式決定的。如果以後想將這些數據讀回賦給程序中相應的變量,就會出現問題,因為無法分隔兩個相鄰的數據。為解決此問題,可在輸出時人為地加入空格。如
for(int i=0;i<3;i++)
strout<<" "<<stud[i].num<<" "<<stud[i].name<<" "<<stud[i].score;
同時應修改流緩沖區的大小,以便能容納全部內容,今改為字節。這樣,運行時將輸出:
1001 Li 78 1002 Wang 89.5 1004 Fun 90
再讀入時就能清楚地將數據分隔開。
[例13.18] 在一個字符數組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;
}
運行結果如下:
array c: 12 34 65 -23 -32 33 61 99 321 32(字符數組c原來的內容)
array a: 12 34 65 -23 -32 33 61 99 321 32 (整型數組a的內容)
array c: -32 –12 32 33 34 61 65 99 321 (字符數組c最後的內容)
對字符串流的幾點說明:
1) 用字符串流時不需要打開和關閉文件。
2) 通過字符串流從字符數組讀數據就如同從鍵盤讀數據一樣,可以從字符數組讀入字符數據,也可以讀入整數、浮點數或其他類型數據。如果不用字符串流,只能從字符數組逐個訪問字符,而不能按其他類型的數據形式讀取數據。這是用字符串流訪問字符數組的優點,使用方便靈活。
3) 程序中先後建立了兩個字符串流strin和strout,與字符數組c關聯。strin從字符數組c中獲取數據,strout將數據傳送給字符數組。分別對同一字符數組進行操作。甚至可以對字符數組交叉進行讀寫,輸入字符串流和輸出字符串流分別有流指針指示當前位 置,互不干擾。
4) 用輸出字符串流向字符數組c寫數據時,是從數組的首地址開始的,因此更新了 數組的內容。
5) 字符串流關聯的字符數組並不一定是專為字符串流而定義的數組,它與一般的字符數組無異,可以對該數組進行其他各種操作。
通過以上對字符串流的介紹,大家可以看到:與字符串流關聯的字符數組相當於內存中的臨時倉庫,可以用來存放各種類型的數據(以ASCII形式存放),在需要時再從中讀回來。它的用法相當於標准設備(顯示器與鍵盤),但標准設備不能保存數據,而字符數組中的內容可以隨時用ASCII字符輸出。它比外存文件使用方便,不必建立文件(不 需打開與關閉),存取速度快。但它的生命周期與其所在的模塊(如主函數)相同,該模塊的生命周期結束後,字符數組也不存在了。因此只能作為臨時的存儲空間。