程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++流概述

C++流概述

編輯:關於C++

在程序設計中,數據輸入/輸出(I/O)操作是必不可少的,C++語言的數據輸入/輸出操作是通過I/O流庫來實現的。C++中把數據之間的傳輸操作稱為流,流既可以表示數據從內存傳送到某個載體或設備中,即輸出流,也可以表示數據從某個載體或設備傳送到內存緩沖區變量中,即輸入流。

 

C++流涉及以下概念:

標准I/O流:內存與標准輸入輸出設備之間信息的傳遞;文件I/O流:內存與外部文件之間信息的傳遞;字符串I/O流:內存變量與表示字符串流的字符數組之間信息的傳遞。

STL中定義的流類:

流類分類 流類名稱 流 類 作 用 流基類 ios 所有流類的父類,保存流的狀態並處理錯誤 輸入流類 istream 輸入流基類,將流緩沖區中的數據作格式化和非格式化之間的轉換並輸入   ifstream 文件輸入流類   istream_withassign cin輸入流類,即操作符>>輸入流   istrstream 串輸入流類, 基於C類型字符串char*編寫   istringstream 串輸入流類, 基於std::string編寫 輸出流類 ostream 輸出流基類,將流緩沖區中的數據作格式化和非格式化之間的轉換。並輸出   ofstream 文件輸出流類   ostream_withassign Cout、cerr、clog的輸出流類,即操作符<<輸出流   ostrstream 串輸入流類, 基於C類型字符串char*編寫   ostringstream 串輸入流類, 基於std::string編寫 輸入/輸出流類 iostream 多目的輸入/輸出流類的基類   fstream 文件流輸入/輸出類   strstream 串流輸入/輸出類, 基於C類型字符串char*編寫   stringstream 串流輸入/輸出類, 基於std::string編寫

注,對於串流,提供了兩套類,一個基於C類型字符串char *編寫(定義於頭文件strstream),一個基於std::string編寫(定義於sstream), 後者是C++標准委員會推薦使用的。另外看一些資料,都說還定義了文件流和串流的基類fstreambase和strstreambase,而且描述ofstream繼承於ostream和fstreambase,但沒有找到,於是省略了。可搜索關鍵字(fstreambase或strstreambase)查看詳情。

預定義標准流對象:

istream &cin; //鍵盤
ostream &cout; //屏幕
ostream &cerr; //屏幕
ostream &clog; //打印機
wistream &wcin;
wostream &wcout;
wostream &wcerr;
wostream &wclog;

C++中讀取string對象

1.標准輸入讀取:cin >> string

a.忽略開頭所有的空白字符(空格、換行、制表符等);

b.讀取字符直至再次遇到空白字符,讀取終止;

2.讀取整行文本:getline(istream, string)

a.不忽略開頭的空白字符;

b.讀取字符直至遇到換行符,如果第一個字符是換行符,則返回空string;

c.返回時丟棄換行符,換行符不存儲在string中。

STL C++中string、ifstream、stringstream的使用

1、從標准輸入接受字符串,然後進行相關處理

#include
#include
#include
using namespace std;
int main()
{
string s; //定義一個stirng對象,從標准輸入接受一個字符串
cout<<"請輸入一行字符串:"< getline(cin,s);
stringstream ss(s); //定義一個string流(使用s實例化)
cout<<"處理後的字符串為:"< for(string s1;ss>>s1;cout< return 0;
}

運行結果如下:

請輸入一行字符串:
you are a good boy
處理後的字符串為:
you
are
a
good
boy

根據前文所說“忽略開頭空白字符,讀取字符直至再次遇到空白字符為止”,這樣的結果不難理解。

2、從文件讀入字符串,然後進行相關處理

#include
#include
#include
#include
using namespace std;
int main()
{
ifstream fin("test.in"); //定義一個文件輸入流,從文件中讀取數據
if(!fin)
{
cout<<"can not open the input file";
return -1;
}
string s; //定義一個string,獲取文件輸入流中的一行
while(getline(fin,s))
{
stringstream ss(s); //定義一個string流(使用一個string對象填充)
int a,b;
ss>>a;ss>>b; //將string流中的兩個值分別讀入a、b中
cout<<"該行數據和為:"< }
return 0;
}

test.in文件內容如下:

1 10
2 100
3 8
4 9

運行結果為:

該行數據和為:11
該行數據和為:102
該行數據和為:11
該行數據和為:13

其中,getline函數原型如下:

template
basic_istream&getline(
basic_istream & is,
basic_string& str);
template
basic_istream&getline(
basic_istream & is,
basic_string& str,
E delim);

 

第二個重載函數很有意思,結尾是char, 或wchar型的一個分隔符,如果設為’\n’,則為以換行符為分隔,如果設為’,',則為以逗號為分隔。由此,雖然C++中的字符串沒有分割函數,如果是從文件中讀取出被特定分隔符分隔的文本,那麼就可以用此方法,如:

std::ifstream file;
file.open("tt.txt");
std::string s,t;
while(std::getline(file,s)) //按行讀取
{
std::stringstream strs(s); //把行裝到另一個流中
while(std::getline(strs,t,’,')) //把流按分隔符實現分割
std::cout< }
file.close();

上面的程序相當於將整個文本先按行分割,再按分隔符分割,也可以變換一下,只按分隔符分割,然後過濾掉按行符,換行符與某元素連在了一起,並處於開頭。

C++的流操作復制文件

寫.wrl, .obj文件格式轉換時用到,記錄一下相關方法

使用C++標准程序庫的輸入輸出流(I/O Stream)復制文件,存在許多的方法,

方法一:逐個字符復制

#include < fstream >
std::ifstream input("in",iOS::binary);
std::ofstream output("out",ios::binary);
char ch;
while (input.get(ch)) output << ch;

注意:如果使用input>>ch讀取字符,則必須先調用input.unsetf(ios::skipws)取消輸入流默認的跳過空白符(空格、換行、制表符等)的輸入格式,因為換行符是空白符的一種。另外,對於ofstream對象,默認的操作方式是ios_base::out | ios_base::trunc,即輸出和文件清空!

方法二:逐行復制

#include < fstream >
#include < string >
std::ifstream input("in",ios::binary);
std::ofstream output("out",ios::binary);
std::string line;
while (getline(input,line)) output << line << "\n";

注意:這裡的代碼有一個小小的缺陷,如果文件不是純文本格式的文件,或者文本文件的最後沒有換行符,那麼會導致復制後的文件末尾添加了一個多余的換行符。

方法三:迭代器復制

#include < fstream >
#include < iterator >
#include < algorithm >
std::ifstream input("in",ios::binary);
std::ofstream output("out",ios::binary);
input.unsetf(ios::skipws);
copy(istream_iterator(input),istream_iterator(),ostream_iterator(output,""));

同樣這裡也有一個小技巧,輸入流的格式默認為跳過空白字符,因此調用unsetf取消這個格式,才可保證正確的復制。見後方中字符串緩沖與文件緩沖類的定義,文件緩沖類沒有定義自己的迭代器,所以就用了

方法四:緩沖區復制

#include < fstream >
std::ifstream input("in",ios::binary);
std::ofstream output("out",ios::binary);
output << input.rdbuf();

這裡直接使用了輸入流的緩沖區,因此沒有引入額外的臨時對象。

很顯然,上述四種方法中,最後一種方法最簡潔,由於直接操作輸入流的緩沖區,從運行效率上來說,也比其他方法有著略微的優勢(當然,由於操作系統可能提供了 額外的基於設備的文件緩沖機制,也許你無法證實這一點)。因此,除非要對輸入內容進行處理,直接復制文件推薦最後一種方法,既不容易出錯,又能獲得良好的性能。

另外,對文件進行更改、刪除、插入等操作,可以直接用以上方法也可以先把文件讀入vector,處理後再輸出,不過當文件很大的時候,vector占用內存空間較大,而且輸出時會破壞原文件格式,盡量不使用。

以上是幾種同種流(文件流)之間的數據復制的方式,字符串流與文件流之間也可以以此方式進行復制。另外,再看一下get函數:

int_typeget();
basic_istream&get(E& c);
basic_istream&get(E *s, streamsize n);
basic_istream&get(E *s, streamsize n, E delim);
basic_istream&get(basic_streambuf *sb);
basic_istream&get(basic_streambuf *sb, E delim);

delim表示結束符,前文中討論的流分割函數還記得吧?又多了種方法,估計get與全局函數getline(不是那個成員函數,成員函數要求給出streamsize,而全局的就不用)實現得差不多。默認的也是’\n’,按行分隔。

C++流緩沖區的應用——輸出文件內容的方法舉例

簡單討論C++流對象的底層緩沖區,並舉例介紹如何使用該緩沖區進行文件內容的輸出

C++標准庫封裝了一個緩沖區類streambuf,以供輸入輸出流對象使用。每個標准C++輸出輸出流對象都包含一個指向streambuf的指針,用戶可以通過調用rdbuf()成員函數獲得該指針,從而直接訪問底層streambuf對象。因此,可以直接對底層緩沖區進行數據讀寫,從而跳過上層的格式化輸入輸出操作。

template class basic_ios
: public ios_base {
basic_streambuf<_Elem, _Traits>*_Mystrbuf;
//C++標准庫封裝了一個緩沖區類streambuf。
_Mysb * rdbuf() const
{ // return stream buffer pointer
return (_Mystrbuf);
}
//使調用者與參數(流緩沖指針)關聯,
//返回自己當前關聯的流緩沖區指針, 可用來重定向等
_Mysb * rdbuf(_Mysb *_Strbuf)
{ // set stream buffer pointer
_Mysb *_Oldstrbuf = _Mystrbuf;
_Mystrbuf = _Strbuf;
return (_Oldstrbuf);
}
};

對象通過調用rdbuf()獲得了底層streambuf對象的指針,也就可以通過該指針調用streambuf支持你各種操作進行輸入輸出。在這裡主要介紹如何利用該指針實現文件內容的輸出。

對於文件流類和字符串流類,分別派生了相應的流緩沖區類型:

template class _Traits> classbasic_streambuf; typedef basic_streambuf >streambuf; typedef basic_streambuf >wstreambuf;
template , class Alloc = allocator > classbasic_stringbuf:
public basic_streambuf
typedef basic_stringbuf, allocator >stringbuf; typedef basic_stringbuf, allocator >wstringbuf;
template > classbasic_filebuf: public basic_streambuf
typedef basic_filebuf >filebuf;
typedef basic_filebuf >wfilebuf;

輸出流提供了一個重載版本operator<<,它以streambuf指針為參數,實現把streambuf對象中的所有字符輸出到輸出流出中;輸入流也提供了一個對應的operator>>重載版本,把輸入流對象中的所有字符輸入到streambuf對象中。輸入流的get成員重載版本中還有以streambuf指針為參數的版本,也可以用來把輸入流的東西寫入到輸出流緩沖區中。

下面用三種方法實現把一個文件的內容輸出標准輸出(當然還可以通過普通的標准格式化輸入輸出完成):

方法一:通過operator<<

#include
#include
using namespace std;
int main()
{
ifstream fin("source.dat");
cout< return 0;
}

方法二:利用get成員函數

ifstream fin("source.dat");
while (!fin.get(*cout.rdbuf()).eof()) { // extract a line input
if (fin.fail()) // blank line
fin.clear();
cout< }

代碼解釋:由於上面代碼中的get版本在遇到’\n’字符時,結束提取,所以需要用循環實現整個文件內容的輸出。另外,當此版本get函數遇到空行時,因為沒有提取到任何字符(注意:get不提取回車符),注意會設置失敗標志ios::failbit,所以此時應當調用clear()函數清除錯誤標志。同樣,因為該版本get不會提取回車符,所以需要用另一版本的get()提取回車符。不同版本的Get函數參見前文。此處還使用了*cout.rdbuf(),cout是ostream類的對象,當然就有緩沖區,可以用rdbuf返回緩沖區對象的指針。最後,關於fin.clear, 需要特別注意的是:要清空流類對象的內存,不能用clear方法,那只是設置了錯誤標志位;

方法三:利用重載的get函數

ifstream fin("main.cpp");
fin.get(*cout.rdbuf(), EOF);

代碼解釋:這個版本的get成員函數可以自定義提取終止符。這裡通過設置為文件結束符(EOF)來達到一下提取整個文件的目的。

當然,你可以把上面的cout換成任意的輸出流,比如文件輸出流,從而可以實現文件的拷貝功能。

另外,上面代碼中並沒有使用輸入流的>>操作符,因為>>和<<是相對的,只是把操作數交換一下位置罷了。因此,你可以把上面代碼中用out<>out.rdbuf(),代碼的功能完全一樣,效果也一樣。

 

 

關於流緩沖區

1.緩沖類型。

標准庫提供緩沖是為了減少對read和write的調用。提供的緩沖有三種類型(整理自APUE):

全緩沖。 在這種情況下,實際的I/O操作只有在緩沖區被填滿了之後才會進行。對駐留在磁盤上的文件的操作一般是有標准I/O庫提供全緩沖。緩沖區一般是在第一次對流進行I/O操作時,由標准I/O函數調用malloc函數分配得到的。flush描述了標准I/O緩沖的寫操作。緩沖區可以由標准I/O函數自動flush(例如緩沖區滿的時候);或者我們對流調用fflush函數。

行緩沖。 在這種情況下,只有在輸入/輸出中遇到換行符的時候,才會執行實際的I/O操作。這允許我們一次寫一個字符,但是只有在寫完一行之後才做I/O操作。一般的,涉及到終端的流–例如標注輸入(stdin)和標准輸出(stdout)–是行緩沖的。

無緩沖。 標准I/O庫不緩存字符。需要注意的是,標准庫不緩存並不意味著操作系統或者設備驅動不緩存。

ISO C要求:

當且僅當不涉及交互設備時,標准輸入和標准輸出是全緩存的。

標准錯誤絕對不是全緩存的。

但是,這並沒有告訴我們當標准輸入/輸出在涉及交互設備時,它們是無緩存的還是行緩存的;也沒有告訴我們標准錯誤應該是行緩存的還是無緩存的。不過,大多數實現默認的緩存類型是這樣的:

標准錯誤總是無緩存的。

對於所有的其他流來說,如果它們涉及到交互設備,那麼就是行緩存的;否則是全緩存的。

2.改變默認緩存類型,即自定義緩沖區

a. 對於C中文件操作:

可以通過下面的函數改變緩存類型(摘自APUE):

void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

這些函數必須在流打開之後、但是未對流做任何操作之前被調用(因為每個函數都需要一個有效的文件指針作為第一個參數)。

利用setbuf,可以打開或者關閉緩存。為了打開緩存,buf參數必須一個大小為BUFSIZ的緩存,BUFSIZ是定義在stdio.h中的常量。要求:BUFSIZ至少為256。如果要關閉緩存,可以將buf設成NULL。

利用setvbuf,我們可以設定緩存類型。這是通過mode參數指定的。

b. 對於C++中流:

virtual basic_streambuf *setbuf(E *s, streamsize n);

第一個參數指向自定義緩沖區空間,第二個為緩沖區大小。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved