C++ primer Plus 第十七章——輸入、輸出和文件

17.1.1流和緩衝區

C++程序把輸入和輸出看作字節流(若干字節組成的字符序列)。輸入時,程序從輸入流中抽取字節;輸出時,程序將字節插入到輸出流中。

輸入流中的字節可能來自鍵盤以及存儲設備或其他程序

輸出流中的字節可以流向屏幕、打印機、存儲設備或其他程序

充當了程序和流源或流目標之間的橋樑,打個比方說,水滴就是字節,水滴匯成的水流就是流,我們可以從水流中取水,也可以往水流中加水,也就是從流中提取輸出的內容,插入輸入的內容。

緩衝區:用作中介的內存塊,它是將信息從設備傳輸到程序或從程序傳輸到設備的臨時存儲工具,可以提高數據的讀取速率

具體原理:內存是放進程運行時的程序和數據的,CPU訪問內存比訪問硬盤速度快,每次從磁盤文件中讀取一個字符,要消耗大量的時間,緩衝方法是從硬盤上讀取大量信息,將信息存在緩衝區裏,因爲CPU訪問內存所用時間比訪問硬盤少,所以這種方法更快。

輸出時,程序首先填滿緩衝區,然後把整塊的數據傳輸給硬盤,並清空緩衝區,這被稱爲刷新緩衝區

程序在什麼時候刷新緩衝區?

用戶按下換行符

輸入的到來時(當程序到達輸入語句時,將刷新緩衝區中當前所有輸出)

 

17.1.2流、緩衝區和iostream文件

爲了管理流,C++提供了一系列頭文件,它們的關係如下

 

streambuf類爲緩衝區提供了內存,並提供了填充緩衝區、訪問緩衝區、刷新緩衝區和管理緩衝區內存的方法

ios_base類表示流的一般特徵,如是否可讀取,是二進制流還是文本流

ostream類提供了輸出方法

istream類提供了輸入方法

iostream類提供了輸入和輸出方法

 

C++爲了能夠處理需要16位國際字符集或更寬的字符類型,在傳統的8位char(“窄”)類型的基礎上添加了wchar_t(“寬”)類型,這也意味着有專門的輸入輸出對象用於處理寬字符如wcin wcout(這個地方有興趣的可以區查一下兩種類型的區別)

iostream頭文件會自動生成八個流對象(四個是寬類型的,這裏只列出窄類型的)

cin

對應於標準輸入流,平時就是用cin輸入各種數據類型,也可以通過重載使其輸入用戶自定義的類

cout

對應於標準輸出流,就是用cout輸出各種數據類型,也可以通過重載使其輸出用戶自定義的類

cerr

對應着標準錯誤流,可用於顯式錯誤信息,默認情況下,這個流被關聯到標準輸出設備(顯示器),這個流沒有被緩衝,這意味着信息將直接發送給屏幕

clog

對應着標準錯誤流,可用於顯式錯誤信息,默認情況下,這個流被關聯到標準輸出設備(顯示器),與cerr不同的是,clog有緩衝區

 

17.2用cout進行輸出

C++將輸出看作字符流,這說明如果要在屏幕上顯示數字-2.34,需要五個字符,-、2、.、3、4。ostream類的主要任務就是將數值類型轉換爲文本形式表示的字符流。

C++通過重載<<運算符使之能夠識別c++所有的基本類型

就是所cout可以用於輸出C++的各種基本類型

1.輸出和指針

C++用指向字符串存儲位置的指針來表示字符串,所以下列cout語句都可顯示字符串

char *p = "123";
char name[20] = "1234";
cout<<"hello";
cout<<name;
cout<<p;

如何輸出指針地址呢

可以用void*將char*轉化爲其他類型來輸出其地址

如cout<<(void *)p;

這樣將會輸出p指向的地址

2.拼接輸出

可以通過cout<<a<<b<<c;這種方式輸出

原因是 cout<<a返回一個cout的對象,可以繼續輸出

 

17.2.2其他ostream方法

cout.put()輸出一個字符,相當於putchar()

cout.write(char*,x)輸出指定字符串的指定數量的字符,注意如果x超出了指定字符串的長度,它仍然會輸出,即輸出垃圾值

舉個例子

cout.put('a');
//輸出字符a
cout.put("1234",2);
//輸出12

 

17.2.3刷新輸出緩衝區

每次等緩衝區滿了再輸出太麻煩了,我們可以使用endl或者flush來刷新緩衝區

具體語法如下

cout<<"132"<<endl;

或者

cout<<"132"<<flush;(<<重載,會將其替換爲flush(cout))

 

17.2.4用cout進行格式化

ostream插入運算符將值轉換爲文本格式。在默認情況下,格式化值的方式如下。

    *  對於char值,如果它代表的是可打印字符,則將被作爲一個字符顯示在寬度爲一個字符的字段中。

    *  對於數值整型,將以十進制方式顯示在一個剛好容納該數字及負號(如果有的話)的字段中;

    *  字符串被顯示在寬度等於該字符串長度的字段中。

  浮點數的默認行爲有變化。下面詳細說明了老式實現和新式實現之間的區別。

    *新式:浮點類型被顯示爲6位,末尾的0不顯示(注意,顯示的數字位數與數字被存儲時精度設置沒有任何關係)。數字以定點表示法顯示還是科學計數法表示,取決於它的值。具體來說,當指數大於等於6或小於等於-5時,將使用科學計數法表示。另外,字段寬度恰好容納數字和負號(如果有的話)。默認的行爲對應於帶%g說明符的標準C庫函數fprintf()。

    *老式:浮點類型顯示爲帶6位小數,末尾的0不顯示(注意,顯示的數字位數與數字被存儲時的精度沒有任何關係)。數字以定點表示法顯示還是以科學計數法表示,取決於他的值。另外,字段寬度恰好容納數字和負號(如果有的話)。

 

1.輸出十進制 八進制 十六進制

要控制整數以十進制、十六進制還是八進制顯示,可以使用dec、hex和oct控制符。

例如,下面的函數調用將cout對象的計數系統格式狀態設置爲十六進制:

hex(cout);設置輸出爲十六進制

oct(cout);設置輸出爲八進制

dec(cout);設置輸出爲十進制

由於stream類重載了<<,所以可以直接使用cout<<hex;來設置輸出爲十六進制

#include <iostream>
using namespace std;

int main()
{
    int a = 20;
    cout<<hex<<a<<endl;
    cout<<oct<<a<<endl;
    cout<<dec<<a<<endl;
    return 0;
}

/*輸出爲
14
24
20
*/

注意設置完輸出16進制之後,要想輸出10進制需要設置回來。

2.調整字段寬度

可以使用width成員函數將長度不同的數字放到寬度相同的字段中,該方法的原型爲:

    int width();

    int width(int i);

  第一種格式返回字段寬度的當前設置;第二種格式將字段寬度設置爲i個空格,並返回以前的字段寬度值。這使得能夠保存以前的值,以便以後恢復寬度值時使用。

  width()方法之影響顯示的下一個項目,然後字段寬度將恢復爲默認值。由於width()是成員函數,因此必須使用對象來調用它。

#include <iostream>
using namespace std;

int main()
{
    int a = 20;
    cout.width(5);
    cout<<a<<a<<endl;
    return 0;
}
/*輸出
   20
*/

3.填充字符

 在默認情況下,cout使用空格填充字段中未被使用的部分,可以使用fill()成員函數來改變填充字符。例如,下面的函數調用將填充字符改爲星號:
    cout.fill('*');

#include <iostream>
using namespace std;

int main()
{
    int a = 20;
    cout.width(5);
    cout.fill('*');
    cout<<a<<a<<endl;
    return 0;
}
/*輸出
***20
*/

fill填充字符會一直有效,直到修改它爲止

4.設置浮點數的顯示精度

 浮點數精度的含義取決於輸出模式。在默認情況下,它指的是顯示的總位數。在定點模式和科學模式下,精度指的是小數點後面的位數。已經知道,C++的默認精度爲6位(但末尾的0將不顯示)。precision成員函數使得能夠選擇其他值。例如,下面的函數調用將cout的精度設置爲2:

    cout.precision(2);

  和width()的情況不同,但與fill()相似,新的精度設置將一直有效,直到被重新設置。下面的程序說明了這一點:

#include <iostream>
using namespace std;

int main()
{
    cout.precision(2);
    double a = 1.28;
    double b = 0.222;
    double c = 11.2;
    cout<<a<<endl;
    cout<<b<<endl;
    cout<<c<<endl;
    return 0;
}
/*輸出結果
1.3
0.23
11
*/

這說明precision會進行四捨五入

5.打印末尾的0和小數點

對於有些輸出,希望保留末尾的0,iostream系列類沒有提供專門用於完成這項任務的函數,但ios_base類提供了一個setf()函數(用於set標記),能夠控制多種格式化特性。這個類還定義了多個常量,可以作爲函數的參數。例如,下面的函數調用使cout顯示末尾的小數點:

    cout.setf(ios_base::showpoint);

  使用默認的浮點格式時,上述語句還將導致末尾的0被顯示出來。

注意cout.setf(ios_base::showpoint);要結合precision使用

  showpoint是ios_base類聲明中定義的類級靜態常量。類級意味着如果在成員函數定義的外面使用它,則必須在常量名前加上作用域運算符(::)。因此,ios_base::showpoint指的是在ios_base類中定義的一個常量。

#include <iostream>
using namespace std;

int main()
{
    cout.setf(ios_base::showpoint);
    double a = 1.28;
    double b = 0.225;
    double c = 11.2;
    cout<<a<<endl;
    cout<<b<<endl;
    cout<<c<<endl;
    return 0;
}
/*輸出

1.28000
0.225000
11.2000
*/

頭文件iomanip

iomanip提供了很多函數來設置cout,如

setprecision()設置精度

setfill()填充字段

setw()設置字段寬度

例子:

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    double a = 1.222222;
    cout<<setw(10)<<a<<endl;
    cout<<setfill('*')<<a<<endl;
    cout<<setw(10)<<setfill('*')<<a<<endl;
    cout<<setprecision(2)<<a<<endl;
    return 0;
}
/*輸出結果
   1.22222
1.22222
***1.22222
1.2
*/

17.3使用cin進行輸入

cin支持C++的各種基本類型 int char double 等等

cin可以連續輸入數據 如cin>>a>>b>>c

cin可以標註輸入的數字是什麼進制  如 cin>>hex>>a 輸入的數字是一個十六進制

 

17.3.1cin>>如何檢查輸入

cin將跳過空白(空格、換行符和製表符),直到遇到非空白字符

int main()
{
    char a[10];
    cin>>a;
    cout<<a;
    return 0;
}

例如,如果是字符串

輸入     123

輸出爲123

cin遇到換行會停止讀入,並把換行符留在緩衝區

int main()
{
    char a[10];
    cin>>a;
    char c = cin.get();
    cout<<(int)c;
    return 0;
}

/*輸入123

輸出爲
10   10是換行的ascii碼值

cin將讀取從非空白字符開始,到與目標類型不匹配的第一個字符之間的全部內容,並把剩餘沒有讀入的留在緩衝區

int main()
{
    int a;
    cin>>a;
    char c = cin.get();
    cout<<a<<endl;
    cout<<c;
    return 0;
}

/*輸入123z


輸出
123
z
*/

運算符將讀取1、2、3,因爲它們是整數的部分,但是不會讀取z,所以z會留在緩衝區裏,下一個cin從這裏開始讀,與此同時,運算符將字符序列123轉換爲一個整數值 賦給a

 

17.3.2流狀態

1設置狀態

cin或cout對象包含一個描述流狀態的數據成員,流狀態由三個ios_base元素組成:eofbit,failbit,badbit。每一個都有0和1兩個值

1是表明發生了這個問題,都將停止cin的輸入,並且只有在clear()方法來回調之後,纔可以繼續使用cin輸入數據

eofbit = 1 說明到達文件末尾

failbit = 1 說明cin操作未能讀取到預期的字符或者 I/O失敗(試圖讀取不可訪問的文件或試圖寫入受保護的磁盤)

badbit = 1 發生了其他錯誤

clear(eofbit)將會使eofbit爲1

2.I/O異常

3.流狀態的影響

只有在流狀態良好的情況(即使eofbit failbit badbit均爲0)cin才能進行輸入

int main()
{
    int a;
    while(cin>>a)
    {
        ;
    }
    if(cin.eof())
        cout<<"到達文件末尾"<<endl;
    return 0;
}

使用ctril+z來告訴程序到達了文件末尾,從而使eofbit = 0

如果發生的是fail錯誤,不僅要使用clear()來使failbit =0,還要清空緩存區

#include <iostream>
using namespace std;

int main()
{
    int a,c;
    while(cin>>a)
    {
        ;
    }
    cin.clear();
    while(!isspace(c = cin.get()))讀取緩存區裏的字符,一直到讀取到空字符
        cout<<(char)c<<endl;
    /*
     while((ch = cin.get())!='\n')讀取緩存區裏的字符,一直到讀取到換行符
        cout<<(char)c<<endl;
    */
    cin>>a;
    cout<<a;
    return 0;
}
/*輸入
123z
123

輸出
z
123
*/

17.3.3其他istream類方法

具體見cin cin.get() cin.getline() getline()的區別

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章