程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++學習之七、揭開C++I/O的神秘面紗

C++學習之七、揭開C++I/O的神秘面紗

編輯:C++入門知識

 

揭開C++I/O的神秘面紗

 

 

 

C++通過流(stream)機制來提供比C更先進的輸入輸出方法。每個流都有一個相關聯的源和目的。包括控制台流、文件流、字符串流等。

 

 

 

1.控制台流<iostream>:

 

流輸出:cout<< ,可輸出包含int、指針、double、字符、string等類型。

 

輸出流方法:

 

put() 和write()是原始的輸出方法。這二個方法風別接受一個字符或字符數組,而不是取已經定義了某種輸出行為的對象或者變量。傳遞給這二個方法的數據按照原來的格式輸出,沒有進行任何專門的格式化或者處理。轉義字符(如\n)仍然按照正確的形式輸出,但不會發生多態輸出。

 

flush():向輸出流寫數據時,流不必立即把數據寫到目的中。大部分的輸出流都會進入緩沖區(buffer),或者累積數據,而不是數據一到來就寫出。當下列條件之一發生時,流會刷新輸出(flush),或者寫出累積數據:

 

        a.到達一個標記,比如endl標記。

 

        b.流超出了作用域,因此被撤銷。

 

        c.對應的輸入流請求輸入(也就是說,利用cin輸入時,cout會刷新輸出)。

 

        d.流緩沖區已滿。

 

        e.明確告訴流要刷新輸出其緩沖區。就是flush()方法。

 

            cout<<”abc”;

 

            cout.flush();

 

        不是所有的輸出流都會緩沖。比如,cerr流就沒有對其輸出進行緩沖。

 

 

 

處理輸出錯誤:輸出錯誤可能會在各種情況下產生。可能你試圖向不存在或者已經指定為只讀的文件寫入數據。可能一個磁盤錯誤阻止了寫操作成功執行,或者由於某種原因控制台進入了鎖定狀態。等等。可以調用方法good()來來確定流是否是好的。

 

cout.flush();

 

if(cout.good())

 

  cout<<”cout good!”<<endl;

 

使用good()能夠很容易地獲取關於流有效性的基本信息,但是它不能告訴你為什麼流是不可用的。方法bad(),它提供了更多的信息,如果放回true,表示發生了一個致命的錯誤(不同於諸如文件結束等非致命錯誤)。還有一個方法fail(),如果最近的操作失敗,它返回true,意味著下一個操作也會失敗。

 

cout.flush();

 

if(cout.fail())

 

cerr<<”unable to flush to standard out”<<endl;

 

要重置流的錯誤狀態,使用clear()方法

 

cout.clear();

 

這裡討論的幾種方法也適合其他流。

 

 

 

輸出控制符:C++的流不僅可以識別數據,還可以識別出控制符(manipulator),這些控制符是一些對象,它們可以改變流的行為,而不是為流提供數據,或者除了提供數據之外還可以改變流的行為。比如endl控制符,它封裝了數據與行為。它告訴流輸出一個回車符並刷新輸出其緩沖區。

 

     a.boolalpha和noboolalpha,告訴流把bool類型的值輸出為true或false(boolalpha)和1或0(noboolalpha)。默認為noboolalpha。

 

     b.hex、oct、dec。分別以十六進制、八進制、十進制輸出數字。

 

     c.setprecision。設置輸出小數時的小數位數。這是一個參數化的控制符,可以接受一個參數。

 

     d.setw。設置輸出數值數據時的字段寬度。這是一個參數化的控制符。

 

     e.setfill。數字寬度小於指定的寬度時,這個控制符可以指定填充空位的字符。。這是一個參數化的控制符。

 

     f.Showpoint和noshowpoing。對於沒有小數部分的浮點數或者雙精度數,分別表示顯示和不顯示小數點。

 

流也可以通過一些與之等效的方法來提供同樣的功能,比如setPrecision()。

 

 

 

流輸入:cin>> 它之後捕獲第一個空白符之前的字符。

 

注意,即使cout沒有使用endl或flush()明確刷新緩沖區,文本仍然會寫到控制台上,因為使用cin就會立刻刷新輸入cout緩沖區。

 

輸入方法:

 

get(),此方法允許從流輸入原始數據。僅僅返回流中的下一個字符。經常用於避免使用>>操作符時發生的自動詞法分析。

 

下面可以讀入有幾個單詞組成的字符串:

 

string readName(istream & inStream)

 

{

 

   string name;

 

   while(inStream.good())

 

   {

 

     int text = inStream.get();

 

     if(next == EOF)  break;

 

     name += next;

 

   }

 

return  name;

 

}

 

首先,函數的參數是istream的引用,而不是其const引用,因為從流中讀取數據的方法會改變所讀取的具體流(會改變流的位置),所以它不是const方法。因此不能對const引用調用這些方法。第二,get()返回值存儲在int中,而不是char中,因為get可以返回特殊的非字符值,比如EOF(文件結束)。會隱性轉換為char。

 

另一個版本:

 

string readName(istream & inStream)

 

{

 

   string name;

 

char   text;

 

   while inStream.get(text))

 

   {

 

     name += next;

 

   }

 

   return name;

 

}

 

 

 

unget():對於大部分目的而言,理解輸入流的正確方法是把它看作是單向的清洗槽。數據落入其中並進入變量。方法unget會打破這種模型,它允許把數據推回到清洗槽中。以此unget會引起流後退一個位置,其本質是把最後一個字符讀回到流中。

 

char ch1,ch2,ch3;

 

cin>>ch1>>ch2>>ch3;

 

cin.unget();

 

char ch4;

 

cin>>ch4;

 

//ch4==ch3

 

 

 

putback():就向unget一樣,允許在流中後退一個字符。二者的區別在於,putback取流中要後退的字符作為參數。

 

char ch1;

 

cin>>ch1;

 

cin.putback(ch1);

 

//ch1是輸入流中得下一個字符。

 

 

 

peek():允許預覽下一個值。

 

int next = cin.peek();

 

if(isdigit(next) code;

 

else othercode;

 

 

 

getline():從流中讀入一行不超過指定值的數據。

 

char buffer[max+1];

 

cin.getline(buffer,max);

 

getline方法會刪除換行字符。所得到的字符串不包括換行字符。而get方法會把換行字符留在輸入流中。

 

還有一個getline方法可以用於C++字符串。

 

string myStr;

 

std::getline(cin,myStr);

 

 

 

處理輸入錯誤:不光有前面的good()之類的方法,還有一個eof()方法,它用來判斷是否到了流的結尾(文件結尾)。是,返回true,否則,返回false。

 

下段程序使用了從流中讀取數據和處理錯誤的常用模式。

 

#include<iostream>

 

#include<fstream>

 

#include<string>

 

 

 

using namespace std;

 

 

 

int main()

 

{

 

   int sum = 0;

 

   if(!cin.good())

 

   {

 

     cout<<”Standard input is in a bad state!”<<endl;

 

     exit(1);

 

   }

 

   int number;

 

   while(true)

 

{

 

  cin>>number;

 

  if(cin.good())

 

    sum += number;

 

  else if(cin.eof()) break;

 

  else

 

  {

 

    cin.clear();

 

    string badToken;

 

    cin>>badToken;

 

    cerr<<”WARNING:Bad input encounter:”<<badToken<<endl;

 

}

 

}

 

cout<<”The sum is :”<<sum<<endl;

 

return 0;

 

}

 

 

 

輸入控制符:

 

     a.oolalpha和noboolalpha,告訴流把bool類型的值輸入為true或false(boolalpha)和1或0(noboolalpha)。默認為noboolalpha。

 

     b.hex、oct、dec。分別以十六進制、八進制、十進制讀取數字。

 

     c.skipws和noskipws。告訴流在做詞法分析時,忽略空白符或者把空白符讀入作為一個空白符token。

 

     d.ws。這是一個便利控制符,它只會忽略流中當前位置的一串空白。

 

 

 

2.   輸入與輸出對象

 

可以通過操作符重載,來控制輸入輸出對象的信息。

 

 

 

3.   字符串流

 

字符串流提供了一種對string應用流語義的方法。采用這種方法,可以有一個表示文本數據的內存中流。如果多個線程都向同一個字符串提供數據,或者需要把一個string傳遞給不同的函數,而同時還要維護當前的讀取位置,在這樣一些應用中,這種方法就很有用。因為流有內置的詞法分析功能,所以字符串流對於解析文本也很有用。

 

類ostringstream和istringstream分別用於向字符串寫數據和從字符串讀數據。它們都在<sstream>頭文件中定義的。分別繼承了ostream和istream同樣的行為。

 

 

 

4.   文件流 見C++文件操作學習

 

補充:

 

seek()函數:可以移動到輸入或輸出流的任意位置。輸入流為seekg(),輸出流為seekp()。每種類型的流都有二個查找方法。可以在流中查找絕對位置或相對位置。位置以字節為單位。

 

移動到流的開始位置:ios_base::beg

 

移動到流的結束位置:ios_base::end

 

移動到流的中間常量:ios_base::cur

 

帶一個參數:

 

outStream.seekp(ios_base::beg)或inStream.seekg(ios_base::beg)

 

帶二個參數(第一個參數表示要移多少位置,第二個參數表示移動的起點):

 

移動到相對於流的開頭的第二個字符:

 

outStream.seekp(2, ios_base::beg);

 

移動到相對於流的倒數的第二個字符:

 

isStream.seekg(-2,ios_base::end);

 

 

 

tell()函數:查詢流的當前位置,返回ios_base::pos_type。輸入流為tellg(),輸出流為tellp()。

 

 

 

 

 

5.   鏈接流

 

可以在任何輸入流與輸出流之間建立鏈接。從而提供一種“一旦訪問就刷新輸出”的行為。換句話說,從輸入流請求數據時,與其鏈接的輸出流會自動刷新輸出。這個行為可以用於所有的流,但是對於可能相互依賴的文件流尤其有用。

 

流鏈接用方法tie()來實現。要把輸出流綁定到一個輸入流上,可以在輸入流上調用tie(),並把輸出流的地址傳遞給它。要斷開這個鏈接,傳遞NULL即可。

 

下面這段程序把一個文件的輸出流綁到了另一個文件的輸入流上。也可以把它綁定到同一個文件的輸入流上,不過要同時讀寫一個文件,雙向I/O可能更好。

 

#include<iostream>

 

#include<fstream>

 

#include<string>

 

 

 

using namespace std;

 

 

 

int main()

 

{

 

   ifstream inFile(“input.txt”);

 

   ofstream outFile(“output.txt”);

 

  

 

   inFile.tie(&outFile);

 

 

 

   outFile<<”Hello there!”;

 

   string text;

 

   inFile>>text;

 

return 0;

 

}

 

方法flush()是在ostream基類上定義的,所以也可以把一個輸出流綁定到另一個輸出流上。

 

outFile.tie(&antherOutFile);

 

這種關系表示,每次向一個文件寫數據時,就會向另一個文件寫入已經發送的緩沖數據。可以使用這種機制保持二個相關文件之間的同步。

 

 

 

6.   雙向流

 

前面一直把輸入流,輸出流最為獨立的二個類來討論的。實際上還有一種流可以同時進行輸入輸出。雙向流可以同時作為輸入流輸出流操作。雙向流是iostream的子類,所以也是istream和ostream的子類。

 

通過stringstream類,也可以一雙向方式訪問字符串流。

 

雙向流對於讀位置和寫位置分別有單獨的指針。在讀操作與寫操作之間切換時,需要查找正確的位置。

 

 

 

7.   國際化

 

寬字符:把字符看做是一個字節的問題就是,並不是所有編程語言和字符集都能用8為二進制為或1個字節來表示。幸運的是,C++提供了寬字符,wchar_t。它會像char一樣工作,唯一區別的是字符串和字符直接量都有一個前綴字母L,來指示應該使用寬字符編碼。

 

wchar_t letter = L’m’;

 

string有wstring,ofstream有wofstream,cin、cout、cerr有wcin、wcout、wcerr。等等。

 

 

 

非西方字符集:Unicode編碼。可以查看相關資料。

 

 

 

本地化環境與方面:各國之間表示數據的唯一差別就是字符集。但是即使使用同一個字符集的國家之間也有差別,比如美國與英國之間類似日期和貨幣之類的。

 

標准C++提供了一種內置機制,他可以把特定地方的數據分組到一個本地化環境中。本地化環境是特定位置相關設置的集合。各個設置稱為一個方面(facet)。C++還提供了定制或者增加方面的方法。

 

下面把美國英語本地化環境(en_US)附加到寬字符控制台輸出流上。

 

wcout.imbue(locale(“en_US”));

 

默認的本地化環境不是美國英語,而是經典本地化環境,它使用ANSI C的約定。

 

比如,如果根本沒有設置本地化環境,或者設置默認的本地化環境,並且要輸出一個數字,那麼輸出時不帶任何標點。

 

wcout.imbue(locale(“C”));

 

wcout<<32767<<endl;

 

 

 

32767

 

但是如果使用的本地化環境是美國英語,數字就會用美國英語的標點格式化。

 

wcout.imbue(locale(“en_US”));

 

wcout<<32767<<endl;

 

 

 

32,767

 

大多數操作系統都有一種機制來確定用戶定義的本地化環境,在C++中,可以向本地化環境對象構造函數傳遞一個空字符串,從而由用戶環境創建一個本地化環境。一旦創建了這個對象,就可以查詢本地化環境。

 

locale loc(“”);

 

if(loc.name().find(“en_US”) == string::npos&&loc.name().find(“United States”)==string::npos))

 

   ----doing something----

 

根據本地化環境的名字來確定本地化環境,還不一定能夠正確地確定用戶是否的確在這個地方。,但是可以提供一條線索。

 

 

www.2cto.com

使用方面:

 

可以使用函數是std::use_facet()來獲取特定本地化環境的特定方面。比如,下面用來檢索英國本地化環境的標准貨幣符號方面。(需要使用頭文件<locale>

 

use_facet<moneypunct<wchar_t> >(locale(“en_GB”));

摘自 我和我追逐的夢~~~

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