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

C++ STL 基礎及應用(4) 輸出輸出流

編輯:關於C++

在 C++ 的標准模板庫中提供了一組模板類來支持面向對象的數據的輸入輸出功能,如基本的輸入輸出流 istream類/ostream類,文件輸入輸出流 ifstream類/ofstream類/fstream類,字符串輸入輸出流 stringstream類/istringstream類/ostringstream類等。C++ I/O 還可以對對象進行輸入輸出操作,這些都是 C 所不具備的。這些流都位於名詞空間 std 內,一般都是以 stream為後綴。它們在數據流的處理上具有靈活、完善和強大的功能。

本章將介紹標准輸入輸出流、文件輸入輸出流和字符串輸入輸出流的相關使用。 各流類之間的繼承關系

無需多說,上圖就什麼都清楚了。

C++ STL 基礎及應用(4) 輸出輸出流
#include <iostream> using namespace std; int main() { int i; float f; char s[20]; cin>>i; cin>>f; cin>>s; cout<<"i="<<i<<endl; cout<<"f="<<f<<endl; cout<<"s="<<s<<endl; return 0; }其中,插入符"<<"和提取符">>"可連續使用。對於 cin 而言,數據默認以空格分隔,上面程序中就可以使用 cin>>i>>f>>s 來賦值,可以使用 cout<<"i="<<i<<endl<<"f="<<f<<endl<<"s="<<s<<endl 來輸出。

由於 cin 默認以空格分隔,那麼如何一次性讀取一行字符串如"How are you ?"呢? get 系列函數很好地解決了這個問題。常用的 get 系列函數有三種,如下所示:

(1)get();
讀取輸入流第一個字符的ASCII值。
(2)istream& get(unsigned char * pszBuf,int nBufLen,char delim='\n');
pszBuf:指向字符串緩沖區的指針,用於保存讀取的結果。
nBufLen:指定緩沖區大小。
delim:指定分隔符,可不填,此時默認會'\n'。
(3)istream& getline(unsigned char * pszBuf,int nBufLen,char delim='\n');
參數解釋同(2),區別在於(2)遇到分隔符即停止執行,不讀取分隔符,而(3)將會讀取該分隔符,但是它不會將其存入結果緩存中。

仔細觀察下面兩段代碼的區別,比較結果,體會(2)、(3)的區別。

#include <iostream>
using namespace std;
int main () {
	char s1[100],s2[100];
	cout<<"輸入:?How are you? (Tab鍵) Fine,thank you!"<<endl;
	int a=cin.get();
	cin.get(s1,sizeof(s1),'\t');
	cin.getline(s2,sizeof(s2),'\n');
	cout<<"a:"<<a<<endl<<"s1:"<<s1<<endl<<"s2:"<<s2<<endl;
	return 0;
}
//注意!只有 get() 和 getline()位置發生變化。
#include <iostream>
using namespace std;
int main () {
	char s1[100],s2[100];
	cout<<"輸入:?How are you? (Tab鍵) Fine,thank you!"<<endl;
	int a=cin.get();
	cin.getline(s1,sizeof(s1),'\t');
	cin.get(s2,sizeof(s2),'\n');
	cout<<"a:"<<a<<endl<<"s1:"<<s1<<endl<<"s2:"<<s2<<endl;
	return 0;
}
第一段代碼執行結果為:
63
How are you?
Fine,thank you!
(注意,Fine 之前輸出了制表符)

第二段代碼執行結果為:
63
How are you?
Fine,thank you!

由於'?'的ASCII值為63,因此先輸出63,第二段沒有輸出制表符的原因就是getline()提取了該制表符,但沒有存入結果緩沖。

處理流錯誤 如何判斷輸入輸出操作是否發生錯誤? C++ 對每次輸入輸出操作的結果都記錄了其狀態信息,存儲於變量 int _Mystate 中,編程中通過這些狀態信息即可判斷此次輸入輸出操作正確與否。

獲取狀態信息的函數如下:
int rdstate(); //無參數,返回 _Mystate,通過下面四個函數來判斷輸入輸出操作狀態。

使用下面四個函數來檢測相應狀態:
bool good() const //輸入正確
{ // test if no state bits are set
return (rdstate() == goodbit);
}

bool eof() const //已到達流的末尾
{ // test if eofbit is set in stream state
return ((int)rdstate() & (int)eofbit);
}

bool fail() const //I/O失敗,主要原因是非法數據,但流可以繼續使用
{ // test if badbit or failbit is set in stream state
return (((int)rdstate() & ((int)badbit | (int)failbit)) != 0);
}

bool bad() const //發生了致命性錯誤(或許是物理上的),流無法繼續使用
{ // test if badbit is set in stream state
return (((int)rdstate() & (int)badbit) != 0);
}
其中標志位如下:
static const _Iostate goodbit = (_Iostate)0x0;
static const _Iostate eofbit = (_Iostate)0x1;
static const _Iostate failbit = (_Iostate)0x2;
static const _Iostate badbit = (_Iostate)0x4; 檢測流錯誤的函數使用代碼如下:
#include <iostream>
using namespace std;
int main () {
	int a;
	cin>>a;
	cout<<"輸入狀態信息碼:"<<cin.rdstate()<<endl;
	if(cin.eof())
	{
		cout<<"已到達流尾!"<<endl;
	}
	else
	{
		if(cin.good())
		{
			cout<<"輸入正確!"<<endl;
		}
		if(cin.fail())
		{
			cout<<"輸入數據類型錯誤!"<<endl;
		}	
		if(cin.bad())
		{
			cout<<"致命錯誤!"<<endl;
		}
	}
	return 0;
}
輸入:1
輸出:輸入狀態信息碼:0 輸入正確!

輸入:a
輸出:輸入狀態信息碼:2 輸入數據類型錯誤!

輸入:Ctrl鍵+z鍵(Linux下為Ctrl+d)
輸出:輸入狀態信息碼:3 已到達流尾!

注意,最後一種情況使用 Ctrl+z 來使控制台模擬文件結束符,一般eof()在讀取文件時用到,用來判斷是否已經讀取完畢。

另外,當產生cin.fail()錯誤時,可以通過清除當前緩沖區來繼續使用這個流,使用cin.clear()函數來清除狀態標志位,並用get()讀取這個錯誤的數據以拋棄即可重新使用這個流,具體使用如下所示。
#include <iostream>
using namespace std;
int main () {
	int d[5];
	int n=0;
	while(n<5)
	{		
		cin>>d[n];
		if(cin.fail())
		{
			cout<<"數據類型出錯!將該數據拋棄並繼續讀取!"<<endl;
			cin.clear();    //清空狀態標志位
			cin.get();    //將這個錯誤數據從流中讀出以拋棄
		}
		else
		{
			cout<<"已正確讀取前"<<n+1<<"個數字: ";
			for(int i=0;i<=n;i++)
			cout<<d[i];
			cout<<endl;
			n++;
		}	
	}
	return 0;
}
輸入:
1 a 2 b 3 4 5
輸出:
已正確讀取前1個數字: 1
數據類型出錯!將該數據拋棄並繼續讀取!
已正確讀取前2個數字: 12
數據類型出錯!將該數據拋棄並繼續讀取!
已正確讀取前3個數字: 123
已正確讀取前4個數字: 1234
已正確讀取前5個數字: 12345

文件輸入輸出流 C++ 對於文件讀寫使用的是 ifstream、ofstream 和 fstream 流類,同樣是依照"打開->讀寫->關閉"原語進行操作。文件讀寫所用到的很多常數都在基類 ios 中被定義出來,iftream 類只用於讀取文件數據,ofstream 類只用於寫入數據到文件,fstream類則可用於讀取和寫入文件數據。

文件打開 ifstream、ofstream、fstream 流類都使用構造函數或者 open() 函數打開文件,原型如下:
ifstream(const char* szName,int nMode=ios::in,int nProt=(int)ios_base::_Openprot)
ofstream(const char* szName,int nMode=ios::out,int nProt=(int)ios_base::_Openprot)
fstream(const char* szName,int nMode,int nProt=(int)ios_base::_Openprot)
void ifstream::open(const char* filename,int openmode=ios::in)
void ofstream::open(const char* filename,int openmode=ios::out|ios::trunc)
void fstream::open(const char* filename,int openmode=ios::in|ios::out)

szName:文件名
nMode:打開方式,打開方式有以下幾種:
ios::in 以讀取方式打開文件。
ios:out 以寫入方式打開文件。
ios:app 每次寫入數據時,先將文件指針移到文件尾,以追加數據到尾部。
ios:ate 僅初始時將文件指針移到文件尾,扔可在任意位置寫入數據。
ios:trunc 寫入數據前,先刪除原有文件內容(清空文件),當文件不存在時則先創建一個文件。
ios:binary 以二進制形式打開文件。

nProt:指定文件保護規格說明,默認取 (int)ios_base::_Openprot,即操作系統默認值,指兼容共享模式。

文件關閉 ifstream、ofstream、fstream 流類都使用 close() 函數來釋放文件資源。
close() 無參數,一般來說打開文件與關閉文件都是成對出現的。

文件讀寫

1.讀寫文本文件

文本文件是最常見的操作對象,關鍵是要解決如何按行讀、如何按行寫的問題。
執行程序後能在可執行文件當前目錄下生成一個"test.txt"文件。
#include <iostream>
#include <fstream>
using namespace std;
int main () {
	char s[100];
	ofstream fout("test.txt");
	fout<<"Hello World!"<<endl<<"How are you?"<<endl<<"Fine,thank you!";
	fout.close();

	ifstream fin("test.txt");
	if(!fin)
	{
		cout<<"文件不存在!";
		return 0;
	}
	else
	{
		while(!fin.eof())
		{
		fin.getline(s,sizeof(s));
		cout<<s<<endl;
		}
	}
	return 0;
}

2.讀寫二進制文件

二進制文件讀寫也經常會用到,使用下面的函數來進行二進制文件讀寫。
ostream& write(const char*,int nSize)
istream& read(char*,int nSize)

讀寫二進制文件代碼如下:
(會在可執行文件出產生一個"test.txt文件",右鍵查看屬性即可發現該文件大小為24字節,即結構體 STUDENT 的大小)
#include <iostream>
#include <fstream>
using namespace std;
struct STUDENT
{
	char name[20];
	int ID;
};
int main () {	
	STUDENT t1={"張三",15};
	STUDENT t2;
	ofstream fout("test.txt");
	fout.write((const char*)&t1,sizeof(t1));
	fout.close();
	ifstream fin("test.txt");
	if(!fin)
	{
		cout<<"文件打開失敗!";
		return 0;
	}
	else
	{
		fin.read((char*)&t2,sizeof(t2));
		cout<<t2.name<<endl<<t2.ID;
		fin.close();
	}
	return 0;
}

3.輸入輸出流緩沖

C++ 標准庫封裝了一個緩沖區類 steambuf,以供輸入輸出流對象使用。每個標准 C++ 輸入輸出流對象都包含一個紙箱 streambuf 的指針,用戶可以通過調用 rdbuf() 成員函數來獲得該指針,從而直接訪問底層 streambuf 對象;可以直接對底層緩沖進行數據讀寫,從而跳過上層的格式化輸入輸出操作。但由於類似的功能均可由上層緩沖區實現,因此就不加以論述了。streambuf 最精彩的部分在於它重載了 operator<< 和 operator>>。對於 operator<< 來說,它以 streambuf 指針為參數,實現把 streambuf 對象中的所有字符輸出到輸出流中;對 operator>> 來說,可把輸入流對象中的所有字符輸出到 streambuf 對象中。

打開一個文件並把文件中的內容送到標准輸出流中:
#include <iostream>
#include <fstream>
using namespace std;
int main () {
	ifstream fin("test.txt");
	if(fin)
	{
		cout<<fin.rdbuf();
	}
	fin.close();
	return 0;
}
同樣是將文件內容輸出,這個方法是很簡潔的。當然,如果把 cout<<fin.rdbuf() 左側的標准輸出流改成文件輸出流,則可實現文件的賦值功能等。

4.定位輸入輸出流

流的位置標識有三個:
ios::beg 流的開始位置
ios::cur 流的當前位置
ios::end 流的結束位置

定位函數主要有兩個
istream& seekg(long relativepos,ios::seek_dir dir)
針對輸入流,第一個參數是要移動的字符數目,可正可負,第二個參數是移動方向,即上面流的位置標識中的一個值。含義是字符指針相對於移動方向向前或者向後移動了多少個字符。
ostream& seekp(long relativepos,ios::seek_dir dir)
針對輸出流,含義同 istream& seekg(long relativepos,ios::seek_dir dir)
另外,上述兩個函數只傳一個參數時,表示移動到流的絕對位置,如 seekg(4) 表明移動到流的第四個字符位置。

定位函數使用代碼如下:
#include <iostream>
#include <fstream>
using namespace std;
int main () {
	fstream fout("test.txt",ios::in|ios::out|ios::trunc);
	fout.write("How are you?",12);
	
	fout.seekp(0,ios::beg);    //將指針移到文件頭
	cout<<fout.rdbuf()<<endl;

	fout.seekp(4);    //將指針移到流的第四個位置
	cout<<fout.rdbuf()<<endl;
	
	fout.seekp(0,ios::end);    //將指針移到文件尾
	cout<<fout.rdbuf();

	fout.close();
	return 0;
}
輸出:
How are you?
are you?

(注意!輸出為三行,最後一行沒有內容。)
字符串輸入輸出流 字符串輸入輸出流直接對內存而不是對文件和標准輸出進行操作,它使用與 cin 及 cout 相同的讀取和格式化函數來操縱內存中的數據,所有字符串流類的申明都包含在標准頭文件 <stringstream> 中。標准庫定義了三種類型的字符串流:
istringstream: 字符串輸入流,提供讀 string 功能。
ostringstream: 字符串輸出流,提供寫 string 功能。
stringstream: 字符串輸入輸出流,提供讀寫 string 功能。
利用字符串輸入輸出流,可以把多種基本數據類型組合成字符串,也可以反解字符串給各種變量賦值。 反解字符串給變量賦值、合並多種基本數據類型代碼如下:
#include <iostream>
#include <sstream>
using namespace std;
int main () {
	int a;
	float b;
	string c;
	char d[20];
	string text="1 3.14 hello 你好";
	string temp;
	istringstream sin(text);
	//反解字符串
	sin>>a;
	sin>>b;
	sin>>c;
	sin>>d;
	cout<<a<<endl<<b<<endl<<c<<endl<<d;
	//合並基本類型
	ostringstream sout;
	sout<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
	cout<<sout.str();
	return 0;
}

 

以上就是C++ STL 基礎及應用(4) 輸出輸出流的全文介紹,希望對您學習和使用程序編程有所幫助.
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved