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

c++ io標准庫2

編輯:C++入門知識

 

接下來我們來學習一下串流類的基礎知識,什麼叫串流類
  簡單的理解就是能夠控制字符串類型對象進行輸入輸出的類,C++不光可以支持C++風格的字符串流控制,還可以支持C風格的字符串流控制。

  我們先看看看C++是如何對C風格的字符串流進行控制的,C中的字符串其實也就是字符數組,字符數組內的數據在內存中的位置的排列是連續的,我們通常用char str[size]或者char *str的方式聲明創建C風格字符數組,為了能讓字符數組作為設備並提供輸入輸出操作,C++引入了ostrstream、istrstream、strstream這三個類,要使用他們創建對象就必須包含strstream.h頭文件。
  istrstream類用於執行C風格的串流的輸入操作,也就是以字符串數組作為輸入設備。
  ostrstream類用於執行C風格的串流的輸出操作,也就是一字符串數組作為輸出設備。
  strstream類同時可以支持C風格的串流的輸入輸出操作。

  istrstream類是從istream(輸入流類)和strstreambase(字符串流基類)派生而來,ostrstream是從ostream(輸出流類)和strstreambase(字符串流基類)派生而來,strstream則是從iostream(輸入輸出流類)和和strstreambase(字符串流基類)派生而來。

  他們的繼承關系如下圖所示:

  串流同樣不是標准設備,不會有預先定義好的全局對象,所以不能直接操作,需要通過構造函數創建對象。

類istrstream的構造函數原形如下:
  istrstream::istrstream(const char *str,int size);
  參數1表示字符串數組,而參數2表示數組大小,當size為0時,表示istrstream類對象直接連接到由str所指向的內存空間並以\0結尾的字符串。

  下面的示例代碼就是利用istrstream類創建類對象,制定流輸入設備為字符串數組,通過它向一個字符型對象輸入數據。

  代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者
#include <iostream>
#include <strstream>
usingnamespace std;
intmain()
{
char *name = "www.cndev-lab.com";
int arraysize = strlen(name)+1;
istrstream is(name,arraysize);
char temp;
is>>temp;
cout<<temp;
system("pause");
}

  類ostrstream用於執行串流的輸出,它的構造函數如下所示:

  ostrstream::ostrstream(char *_Ptr,int streamsize,int Mode = ios::out);

  第一個參數是字符數組,第二個是說明數組的大小,第三個參數是指打開方式。

  我們來一個示例代碼:

#include <iostream>
#include <strstream>
usingnamespace std;
intmain()
{
int arraysize=1;
char *pbuffer=newchar[arraysize];
ostrstream ostr(pbuffer,arraysize,ios::out);
ostr<<arraysize<<ends;//使用ostrstream輸出到流對象的時候,要用ends結束字符串
cout<<pbuffer;
delete[] pbuffer;
system("pause");
}

  上面的代碼中,我們創建一個c風格的串流輸出對象ostr,我們將arraysize內的數據成功的以字符串的形式輸出到了ostr對象所指向的pbuffer指針的堆空間中,pbuffer也正是我們要輸出的字符串數組,在結尾要使用ends結束字符串,如果不這麼做就有溢出的危險。

接下來我們繼續看一下C++風格的串流控制,C++引入了ostringstream、istringstream、stringstream這三個類,要使用他們創建對象就必須包含sstream.h頭文件。

  istringstream類用於執行C++風格的串流的輸入操作。
  stringstream類同時可以支持C++風格的串流的輸入輸出操作。
  strstream類同時可以支持C風格的串流的輸入輸出操作。

  istringstream類是從istream(輸入流類)和stringstreambase(c++字符串流基類)派生而來,ostringstream是從ostream(輸出流類)和stringstreambase(c++字符串流基類)派生而來,stringstream則是從iostream(輸入輸出流類)和和stringstreambase(c++字符串流基類)派生而來。

  他們的繼承關系如下圖所示:

  istringstream是由一個string對象構造而來,istringstream類從一個string對象讀取字符。
  istringstream的構造函數原形如下:
  istringstream::istringstream(string str);

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者
#include <iostream>
#include <sstream>
usingnamespace std;
intmain()
{
istringstream istr;
istr.str("1 56.7",);
//上述兩個過程可以簡單寫成 istringstream istr("1 56.7");
cout << istr.str()<<endl;
int a;
float b;
istr>>a;
cout<<a<<endl;
istr>>b;
cout<<b<<endl;
system("pause");
}

  上例中,構造字符串流的時候,空格會成為字符串參數的內部分界,例子中對a,b對象的輸入"賦值"操作證明了這一點,字符串的空格成為了整型數據與浮點型數據的分解點,利用分界獲取的方法我們事實上完成了字符串到整型對象與浮點型對象的拆分轉換過程。

  str()成員函數的使用可以讓istringstream對象返回一個string字符串(例如本例中的輸出操作(cout<<istr.str();)。

  ostringstream同樣是由一個string對象構造而來,ostringstream類向一個string插入字符。
  ostringstream的構造函數原形如下:
  ostringstream::ostringstream(string str);

  示例代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者
#include <iostream>
#include <sstream>
#include <string>
usingnamespace std;
intmain()
{
ostringstream ostr;
//ostr.str("abc");//如果構造的時候設置了字符串參數,那麼增長操作的時候不會從結尾開始增加,而是修改原有數據,超出的部分增長
ostr.put('d');
ostr.put('e');
ostr<<"fg";

string gstr = ostr.str();
cout<<gstr;
system("pause");
}

  在上例代碼中,我們通過put()或者左移操作符可以不斷向ostr插入單個字符或者是字符串,通過str()函數返回增長過後的完整字符串數據,但值得注意的一點是,當構造的時候對象內已經存在字符串數據的時候,那麼增長操作的時候不會從結尾開始增加,而是修改原有數據,超出的部分增長。

對於stringstream了來說,不用我多說,大家也已經知道它是用於C++風格的字符串的輸入輸出的。
  stringstream的構造函數原形如下:

  stringstream::stringstream(string str);

  示例代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者
#include <iostream>
#include <sstream>
#include <string>
usingnamespace std;

intmain()
{
stringstream ostr("ccc");
ostr.put('d');
ostr.put('e');
ostr<<"fg";
string gstr = ostr.str();
cout<<gstr<<endl;

char a;
ostr>>a;
cout<<a

system("pause");
}

  除此而外,stringstream類的對象我們還常用它進行string與各種內置類型數據之間的轉換。

  示例代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者
#include <iostream>
#include <sstream>
#include <string>
usingnamespace std;

intmain()
{
stringstream sstr;
//--------int轉string-----------
int a=100;
string str;
sstr<<a;
sstr>>str;
cout<<str<<endl;
//--------string轉char[]--------
sstr.clear();//如果你想通過使用同一stringstream對象實現多種類型的轉換,請注意在每一次轉換之後都必須調用clear()成員函數。
string name = "colinguan";
char cname[200];
sstr<<name;
sstr>>cname;
cout<<cname;
system("pause");
}

  接下來我們來學習一下輸入/輸出的狀態標志的相關知識,C++中負責的輸入/輸出的系統包括了關於每一個輸入/輸出操作的結果的記錄信息。這些當前的狀態信息被包含在io_state類型的對象中。io_state是一個枚舉類型(就像open_mode一樣),以下便是它包含的值。

goodbit 無錯誤

Eofbit 已到達文件尾

failbit 非致命的輸入/輸出錯誤,可挽回

badbit 致命的輸入/輸出錯誤,無法挽回
有兩種方法可以獲得輸入/輸出的狀態信息。一種方法是通過調用rdstate()函數,它將返回當前狀態的錯誤標記。例如,假如沒有任何錯誤,則rdstate()會返回goodbit.

  下例示例,表示出了rdstate()的用法:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
usingnamespace std;

intmain()
{
int a;
cin>>a;
cout<<cin.rdstate()<<endl;
if(cin.rdstate() == ios::goodbit)
{
cout<<"輸入數據的類型正確,無錯誤!"<<endl;
}
if(cin.rdstate() == ios_base::failbit)
{
cout<<"輸入數據類型錯誤,非致命錯誤,可清除輸入緩沖區挽回!"<<endl;
}
system("pause");
}

  另一種方法則是使用下面任何一個函數來檢測相應的輸入/輸出狀態:

bool bad();

bool eof();

bool fail();

bool good();

  下例示例,表示出了上面各成員函數的用法:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
usingnamespace std;

intmain()
{
int a;
cin>>a;
cout<<cin.rdstate()<<endl;
if(cin.good())
{
cout<<"輸入數據的類型正確,無錯誤!"<<endl;
}
if(cin.fail())
{
cout<<"輸入數據類型錯誤,非致命錯誤,可清除輸入緩沖區挽回!"<<endl;
}
system("pause");
}

  如果錯誤發生,那麼流狀態既被標記為錯誤,你必須清除這些錯誤狀態,以使你的程序能正確適當地繼續運行。要清除錯誤狀態,需使用clear()函數。此函數帶一個參數,它是你將要設為當前狀態的標志值。,只要將ios::goodbit作為實參。

  示例代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
usingnamespace std;

intmain()
{
int a;
cin>>a;
cout<<cin.rdstate()<<endl;
cin.clear(ios::goodbit);
cout<<cin.rdstate()<<endl;
system("pause");
}

通常當我們發現輸入有錯又需要改正的時候,使用clear()更改標記為正確後,同時也需要使用get()成員函數清除輸入緩沖區,以達到重復輸入的目的。

  示例代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者
#include <iostream>
usingnamespace std;

intmain()
{
int a;
while(1)
{
cin>>a;
if(!cin)//條件可改寫為cin.fail()
{
cout<<"輸入有錯!請重新輸入"<<endl;
cin.clear();
cin.get();
}
else
{
cout<<a;
break;
}
}
system("pause");
}

  最後再給出一個對文件流錯誤標記處理的例子,鞏固學習,代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者
#include <iostream>
#include <fstream>
usingnamespace std;

intmain()
{
ifstream myfile("c:\\1.txt",ios_base::in,0);
if(myfile.fail())
{
cout<<"文件讀取失敗或指定文件不存在!"<<endl;
}
else
{
char ch;
while(myfile.get(ch))
{
cout<<ch;
}
if(myfile.eof())
{
cout<<"文件內容已經全部讀完"<<endl;
}
while(myfile.get(ch))
{
cout<<ch;
}
}
system("pause");
}

C語言提供了格式化輸入輸出的方法,C++也同樣,但是C++的控制符使用起來更為簡單方便,在c++下有兩中方法控制格式化輸入輸出。
  1.有流對象的成員函數。
  例如,下列程序以成員函數的方式控制輸出的精度:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
usingnamespace std;

intmain()
{
float pi=3.14159f;
cout<<pi<<endl;
cout.precision(2);
cout<<pi<<endl;
system("pause");
}

  2.使用C++輸入輸出控制符,控制符是在拖文件iomanip.h中定義的對象,與成員函數有一樣的效果,控制符不必像成員函數學那樣單獨調用,它可以直接插入流中使用。
  例如,下列程序以控制符的方式控制輸出的精度:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
#include <iomanip>
usingnamespace std;

intmain()
{
float pi=3.14159f;
cout<<pi<<endl;
cout<<setprecision(4);
cout<<pi<<endl;
system("pause");
}

  下表我們列出了一些比較常用的控制符號,由於篇幅有限讀者請根據自己的需要查閱相關書籍:

  對於iostream標准庫來說包含了眾多的成員函數,各函數都有其自身的作用,篇幅問題筆者在這裡不能一一說明例舉,由於標准輸入對象cin提供輸入的時候會自動以空格作為分界,給我們獲取一行帶有空格的完整字符串帶來了困難,在這裡補充一個非常用有的成員函數----getline()。

  其函數原型為:
  getlin(chiar *str,int size,char='\n');

  第一個參數是字符數組,用於存放整行文本,第二個參數讀取的最大字符個數,第三個參數為作為分界界限的字符,默認識是\n,換行符。

  示例代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
#include <iomanip>
usingnamespace std;

intmain()
{
char str[100];
cin.getline(str,sizeof(str),'\n');
cout<<str<<endl;
system("pause");
}

  通過上面內容的學習,我們對i/o有了一些基本點基本的認識,現在是該切入正題的時候了,詳細學習一下,如何重載左移與右移操作符。

先說左移(<<)操作符,也就是我們常說的輸出操作符
  對於自定義類來說,重載左移操作符的方法我們常使用類的友元方式進行操作。


  示例代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
usingnamespace std;

class Test
{
public:
Test(int age = 0,char *name = "\0")
{
Test::age = age;
strcpy(Test::name,name);
}
void outmembers(ostream &out)
{
out<<"Age:"<<age<<endl<<"Name:"<<this->name<<endl;
}
friendostream& operator <<(ostream& ,Test&);
protected:
int age;
char name[50];
};
ostream& operator <<(ostream& out,Test &temp)
{
temp.outmembers(out);
return out;
}
intmain()
{
Test a(24,"管寧");
cout<<a;
system("pause");
}

  上例代碼中,我們對void outmembers(ostream &out)的參數使用ostream定義主要是為了可以向它傳遞任何ostream類對象不光是cout也可以是ofstrem或者是ostrstream和ostringstream類對象,做到通用性。

  重載運算符,我們知道可以是非成員方式也可以是成員方式的,對於<<來說同樣也可以是成員方式,但我十分不推薦這麼做,因為對於類的成員函數來說,第一個參數始終是會被隱藏的,而且一定是當前類類型的。

  下面的示例代碼就是將上面的<<重載函數修改成成員方式的做法:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
usingnamespace std;

class Test
{
public:
Test(int age = 0,char *name = "\0")
{
Test::age = age;
strcpy(Test::name,name);
}
void outmembers(ostream &out)
{
out<<"Age:"<<age<<endl<<"Name:"<<this->name<<endl;
}
ostream& operator <<(ostream &out)
{
this->outmembers(out);
return out;
}
protected:
int age;
char name[50];
};
intmain()
{
Test a(24,"管寧");
a<<cout;
system("pause");
}

  從代碼實現上,我們將函數修改成了ostream& operator <<(ostream &out),迫不得已將ostream類型的引用參數放到了後面,這是因為,成員方式運算符重載函數第一個參數會被隱藏,而且一定是當前類類型的,這和ostream類型沖突了。由此我們在使用cout輸出的時候就必須寫成a<<cout;,這樣一來代碼的可讀行就大大降低了,這到底是左移還是右移呢?為此我再一次說明,對於左移和右移運算符的重載是十分不推薦使用成員函數的方式編寫的。

為了鞏固學習,下面我們以fstream對象輸出為例做一個練習。

  代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者
#include <iostream>
#include <fstream>
usingnamespace std;

class Test
{
public:
Test(int age = 0,char *name = "\0")
{
Test::age = age;
strcpy(Test::name,name);
}
void outmembers(ostream &out)
{
out<<"Age:"<<age<<endl<<"Name:"<<this->name<<endl;
}
friendostream& operator <<(ostream& ,Test&);
protected:
int age;
char name[50];
};
ostream& operator <<(ostream& out,Test &temp)
{
temp.outmembers(out);
return out;
}
intmain()
{
Test a(24,"管寧");
ofstream myfile("c:\\1.txt",ios::out,0);
if (myfile.rdstate() == ios_base::goodbit)
{
myfile<<a;
cout<<"文件創建成功,寫入正常!"<<endl;
}
if (myfile.rdstate() == ios_base::badbit)
{
cout<<"文件創建失敗,磁盤錯誤!"<<endl;
}
system("pause");
}

  對於左移運算符重載函數來說,由於不推薦使用成員方式,那麼使用非成員方式在類有多重繼承的情況下,就不能使用虛函數進行左移運算符重載的區分,為了達到能夠區分顯示的目的,給每個類分別添加不同的虛函數是必要的。

  示例代碼如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
#include <fstream>
usingnamespace std;

class Student
{
public:
Student(int age = 0,char *name = "\0")
{
Student::age = age;
strcpy(Student::name,name);
}
virtualvoid outmembers(ostream &out) = 0;
friendostream& operator <<(ostream& ,Student&);
protected:
int age;
char name[50];
};
ostream& operator <<(ostream& out,Student &temp)
{
temp.outmembers(out);
return out;
}
class Academician:public Student
{
public:
Academician(int age = 0,char *name = "\0",char *speciality="\0"):Student(age,name)
{
strcpy(Academician::speciality,speciality);
}
virtualvoid outmembers(ostream &out)
{
out<<"Age:"<<age<<endl<<"Name:"<<name<<endl<<
"speciality:"<<speciality<<endl;
}
protected:
char speciality[80];
};
class GraduateStudent:public Academician
{
public:
GraduateStudent(int age = 0,char *name = "\0",char *speciality="\0",
char *investigate="\0"):Academician(age,name,speciality)
{
strcpy(GraduateStudent::investigate,investigate);
}
virtualvoid outmembers(ostream &out)
{
out<<"Age:"<<age<<endl<<"Name:"<<name<<endl<<
"speciality:"<<speciality<<endl<<"investigate:"<<investigate<<endl;
}
protected:
char investigate[100];
};
intmain()
{
Academician a(24,"管寧","Computer Science");
cout<<a;
GraduateStudent b(24,"嚴燕玲","Computer Science","GIS System");
cout<<b;
system("pause");
}

  在上面的代碼中為了能夠區分輸出a對象與b對象,我們用虛函數的方式重載了繼承類Academician與多重繼承類GraduateStudent的outmembers成員函數,由於ostream& operator <<(ostream& out,Student &temp) 運算符重載函數是Student基類的,Student &temp參數通過虛函數的定義可以適應不同派生類對象,所以在其內部調用temp.outmembers(out); 系統可識別不同繼類的outmembers()成員函數。

最後看一下,右移運算符的重載,右移運算符我們也常叫它輸入運算符號,對於它來說,具體實現和左移運算符的重載差別並不大,對於有多成員對象的類來說,只要保證能夠完整輸入各成員對象大數據就可以了。

  示例如下:

//程序作者:管寧
//站點:www.cndev-lab.com
//所有稿件均有版權,如要轉載,請務必著名出處和作者

#include <iostream>
usingnamespace std;

class Test
{
public:
Test(int age = 0,char *name = "\0")
{
Test::age = age;
strcpy(Test::name,name);
}
void inputmembers(istream &out)
{
cout<<"please input age:";
cin>>Test::age;
cout<<"please input name:";
cin>>Test::name;
}
friendistream& operator >>(istream& ,Test&);
public:
int age;
char name[50];
};
istream& operator >>(istream& input,Test &temp)
{
temp.inputmembers(input);
return input;
}
intmain()
{
Test a;
cin>>a;
cout<<a.age<<"|"<<a.name<<endl;
system("pause");
}

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