日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

《C++ Primer Plus》第17章:输入、输出和文件(2)

發布時間:2024/1/18 c/c++ 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《C++ Primer Plus》第17章:输入、输出和文件(2) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

使用 cout 進行輸出

正如前面指出,C++將輸出看作字節流(根據實現和平臺的不同,可能是8位、16位或32位的字節,但都是字節),但在程序中,很多數據被組織成比字節更大的單位。例如,int類型由16位或32位的二進制表示;double 值由64位的二進制數據表示。但在將字節流發送給屏幕時,希望每個字節表示一個字符值。也就是說,要在屏幕上顯示數字-2.34,需要將5個字符(-、2、.、3 和 4),而不是這個值的64位內部浮點表示發送到屏幕上。因此,ostream 類最重要的任務之一是將數值類型(如int或float)轉換為以文本形式表示的字符流。也就是說,ostream類將數據內部表示(二進制位模式)轉換為由字符字節組成的輸出流(以后會有仿生移植物,使得能夠直接翻譯二進制數據。我們把這種開發作為一個練習,留給您)。為執行這些轉換任務,ostream類提供了多個類方法?,F在就來看看它們,總結本書使用的方法,并介紹能夠更精密地控制輸出外觀的其他方法。

重載的<<運算符

本書常結合使用 cout 和 << 運算符(插入(insertion)運算符):

int clients = 22; cout << clients;

在 C++ 中,與 C 一樣,<< 運算符的默認含義是按位左移運算符。表達式 x << 3 的意思,將 x 的二進制表示中所有的位向左移動 3 位。顯然,這與輸出的關系不大。但 ostream 類重新定義了 << 運算符,方法是將其重載為輸出。在這種情況下,<<叫做插入運算符,而不是左移運算符(左移運算符由于其外觀(像向左流動的信息流)而獲得這種新角色)。插入運算符被重載,使之能夠識別 C++ 中所有的基本類型:

  • unsigned char;
  • signed char;
  • char;
  • short;
  • unsigned short;
  • int;
  • unsigned int;
  • long;
  • unsigned long;
  • long long (C++11);
  • unsigned long long (C++11);
  • float;
  • double;
  • long double;

對于上述每種數據類型,ostream類都提供了 operator<<()函數的定義(第11章討論過,名稱中包含運算符的函數用于重載該運算符)。因此,如果使用下面這樣一條語句,而value是前面列出的類型之一,則C++程序將其對應于有相應特征標的運算符函數:

cout << value;

例如,表達式 cout<<88 對應于下面的方法原型:

ostream & operator<<(int);

該原型表明,operator<<()函數接受一個 int 參數,這與上述語句中的 88 匹配。該原型還表明,函數返回一個指向 ostream 對象的引用,這使得可以將輸出連接起來,如下所示:

cout << "I'm feeling sedimental over " << boundary << "\n";

如果您是 C 語言程序員,深受%類型說明符過多、說明符類型與值不匹配時將發生問題等痛苦,則使用 cout 非常簡單(當然,由于有 cin,C++ 輸入也非常簡單)。

  • 輸出和指針
    ostream 類還為下面的指針類型定義了插入運算符函數:

    • const signed char *;
    • const unsigned char *;
    • const char *;
    • void *。

    不要忘了,C++用指向字符串存儲位置的指針來表示字符串。指針的形式可以是char數組名、顯式的char指針或用引號括起的字符串。因此,下面所有的cout語句都顯示字符串:

    char name[20] = "Dully Diddlemore"; char * pn = "Violet D'Amore"; cout << "Hello!"; cout << name; cout << pn;

    方法使用字符串中的終止空字符來確定何時停止顯示字符。

    對于其他類型的指針,C++將其對應于 void *,并打印地址的數值表示。如果要獲得字符串的地址,則必須將其強制轉換為其他類型,如下面的代碼片段所示:

    int eggs = 12;char * amount = "dozen";cout << &egg; // prints address of eggs variablecout << amount; // prints the string "dozen"cout << (void *) amount; // prints the address of the "dozen" string
  • 拼接輸出

    插入運算符的所有化身的返回類型都是 ostream&。也就是說,原型的格式如下:

    ostream & operator<<(type);

    (其中,type 是要顯示的數據的類型)返回類型 ostream & 意味著使用該運算符將返回一個指向 ostream 對象的引用。哪個對象呢?函數定義指出,引用將指向用于調用該運算符的對象。換句話說,運算符函數的返回值為調用運算符的對象。例如,cout << “potluck” 返回的是 cout 對象。這種特性使得能夠通過插入來連接輸出。例如,請看下面的語句:

    cout << "We have " << count << " unhatched chickens.\n";

    表達式 cout << "We have ”將顯示字符串,并返回 cout 對象。至此,上述語句將變為:

    cout << count << " unhatched chickens.\n";

    表達式 cout << count 將顯示 count 變量的值,并返回 cout。然后 cout 將處理語句中的最后一個參數。這種設計技術確實是一項很好的特性,這也是前幾章中重載 << 運算符的示例模仿了這種技術的原因所在。

  • 其他 ostream 方法

    除了各種 operator<<()函數外,ostream類還提供了 put() 方法和 write() 方法,前者用于顯示字符,后者用于顯示字符串。

    最初,put() 方法的原型如下:

    ostream & put(char);

    當前標準與此相同,但被模板化,以適用于 wchar_t??梢杂妙惙椒ū硎痉▉碚{用它:

    cout.put('W'); // display the W character

    其中,cout 是調用方法的對象,put() 是類成員函數。和 << 運算符函數一樣,該函數也返回一個指向調用對象的引用,因此可以用它將拼接輸出:

    cout.put('I').put('t'); // displaying It with two put() calls

    函數調用 cout.put(‘I’) 返回cout,cout然后被用作 put(‘t’) 調用的調用對象。

    在原型合適的情況下,可以將數值型參數(如int)用于 put(),然函數原型自動將參數轉換為正確 char 值。例如,可以這樣做:

    cout.put(65); // display the A character cout.put(66.3); // display the B character

    第一條語句將 int 值 65 轉換為一個 char 值,然后顯示 ASCII 碼為 65 的字符。同樣,第二條語句將 double 值 66.3 轉換為 char 值 66,并顯示對應的字符。

    這種行為在 C++2.0 之前可派上用場。在這些版本中,C++語言用 int 值表示字符常量。因此,下面的語句將‘W’解釋為一個 int 值,因此將其作為整數 87(即該字符的 ASCII值)顯示出來:

    cout << 'W';

    然而,下面這條語句能夠正常工作:

    cout.put('W');

    因為當前的 C++ 將 char 常量表示為 char 類型,因此現在可以使用上述任何一種方法。
    一些老式編譯器錯誤地為 char、unsigned char 和 signed char 3種參數類型重載了 put()。這使得將 int 參數用于 put() 時具有二義性,因為 int 可被轉換這3種類型中的任何一種。

    write() 方法顯示整個字符串,其模板原型如下:

    basic_ostream<charT, traits>& write(const char_type * s, streamsize n);

    write() 的第一個參數提供了要顯示的字符串的地址,第二個參數指出要顯示多少個字符。使用 cout 調用 write() 時,將調用 char 具體化,因此返回類型為 ostream&。下面的程序演示了 write() 方法是如何工作的。

    // write.cpp -- using cout.write() #include<iostream> #include<cstring> // or else string.hint main(){using std::cout;using std::endl;const char * state1 = "Florida";const char * state2 = "Kansas";const char * state3 = "Euphoria";int len = std::strlen(state2);cout << "Increasing loop index:\n";int i;for(i=1; i <= len; i++){cout.write(state2, i);cout << endl;}// concatenate outputcout << "Decreasing loop index:\n";for(i = len; i > 0; i--) {cout.write(state2, i) << endl;}// exceed string lengthcout << "Exceeding string length:\n";cout.write(state2, len+5) << endl;return 0; }

    有些編譯器可能指出該程序定義了數組 state1 和 state3 但沒有使用它們。這不是什么問題,因為這兩個數組只是用于提供數組 state2 前面和后面的數據,以便您知道程序錯誤地存取 state2 時發生的情況。下面是該程序的輸出:

    Increasing loop index: K Ka Kan Kans Kansa Kansas Decreasing loop index: Kansas Kansa Kans Kan Ka K Exceeding string length: KansasEuph

    注意,cout.write() 調用返回 cout 對象。這是因為 wrtie() 方法返回一個指向調用它的對象的引用,這里調用它的對象是 cout。

    這使得可以將輸出拼接起來,因為 cout.write() 將被其返回值 cout 替換:

    cout.write(state2, i) << endl;

    還需要注意的是,wrtie() 方法并不會在遇到空字符時自動停止打印字符,而只是打印指定數目的字符,即使超出了字符串的邊界!在這個例子中,在字符串 “kansas” 的前后聲明了另外兩個字符串,以便相鄰的內存包含數據。編譯器在內存中存儲數據的順序以及調整內存的方式各不相同。例如,“Kansas”占用7個字節,而該編譯器使用4個字節的倍數調整字符串,因此“Kansas”被填充成占用8個字節。由于編譯器之間的差別,因此輸出的最后一行可能不同。

    write() 方法也可用于數值數據,您可以將數字的地址強制轉換為 char*,然后傳遞給它:

    long val = 560031841; cout.write( (char *)&val,sizeof(long));

    這不會將數字轉換為相應的字符,而是傳輸內存中存儲的位表示。例如,4字節的 long 值(如560031841)將作為 4 個獨立的字節被傳輸。輸出設備(如顯示器)將把每個字節作為 ASCII碼進行解釋。因此在頻幕上,560031841 將被顯示為 4 個字符的組合,這很可能是亂碼(也可能不是,請試試看)。然而,wrtie() 確實為將數值數據存儲在文件中提供了一種簡潔、準確的方式,這將在本章后面進行介紹。

    刷新輸出緩沖區

    如果程序使用 cout 將字節發送給標準輸出,情況將如何?由于 ostrream 類對 cout 對象處理的輸出進行緩沖,所以輸出不會立即發送到目標地址,而是被存儲在緩沖區中,直到緩沖區填滿。然后,程序將刷新(flush)緩沖區,把內容發送出去,并清空緩沖區,以存儲新的數據。通常,緩沖區為512字節或其整數倍。當標準輸出連接的是硬盤上的文件時,緩沖可以節省大量的時間。畢竟,不希望程序為發送512個字節,而存取磁盤512次。將512個字節收集到緩沖區中,然后一次性將它們寫入硬盤的效率要高得多。

    然而,對于屏幕輸出來說,首先緩沖區的重要性要低得多。如果必須重述消息“Press any key to continue” 以便使用 512 個字節來填充緩沖區,實在是太不方便了。所幸的是,在屏幕輸出時,程序不必等到緩沖區被填滿。例如,將換行符發送到緩沖區后,將刷新緩沖區。另外,正如前面指出的,多數 C++ 實現都會在輸入即將發生時刷新緩沖區。也就是說,假設有下面的代碼:

    cout << "Enter a number: "; float num; cin >> num;

    程序期待輸入這一事實,將導致它立刻顯示 cout 消息(即刷新“Enter a number: " 消息),即使輸出字符串中沒有換行符。如果沒有這種特性,程序將等待輸入,而無法通過 cout 消息來提示用戶。

    如果實現不能在所希望時刷新輸出,可以使用兩個控制符中的一個來強行進行刷新??刂品?flush 刷新緩沖區,而控制符 endl 刷新緩沖區,并插入一個換行符。這兩個控制符的使用方式與變量名相同:

    cout << "Hello, good-looking! " << flush; cout << "Wait just a moment, please." << endl;

    事實上,控制符也是函數。例如,可以直接調用 flush() 來刷新 cout 緩沖區:

    flush(cout);

    然而,ostream 類對 << 插入運算符進行了重載,使得下述表達式將被替換為函數調用 flush(cout):

    cout << flush;

    因此,可以用更為方便的插入表示法來成功地進行刷新。

    用 cout 進行格式化

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

    • 對于 char 值,如果它代表的是可打印字符,則將被作為一個字符顯示在寬度為一個字符的字段中。
    • 對于數值整型,將以十進制方式顯示在一個剛好容納該數字及負號(如果有的話)的字段中。
    • 字符串被顯示在寬度等于該字符串長度的字段中。

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

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

    因為每個值的顯示寬度都等于它的長度,因此必須顯式地在值之間提供空格;否則,相鄰的值將不會被分開。

    下面的程序演示了默認的輸出情況,它在每個值后面都顯示一個冒號(:),以便可以知道每種情況下的字段寬度。該程序使用表達式 1.0/9.0 來生成一個無窮小數,以便能夠知道打印了多少位。

    注意,并非所有的編譯器都能生成符合當前 C++ 標準格式的輸出。另外,當前標準允許區域性變化。例如,歐洲實現可能遵循歐洲人的風格:使用逗號而不是句點來表示小數點。也就是說,2.54 將被寫成 2, 54。區域庫(頭文件 locale)提供了用特定的風格影響(imbuing)輸入或輸出流的機制,所以同一個編譯器能夠提供多個區域選項。本章使用美國格式。

    // defaults.cpp -- cout default formats #include <iostream>int main(){using std::cout;cout << "12345678901234567890\n";char ch = 'K';int t = 273;cout << ch << ":\n";cout << t << ":\n";cout << -t << ":\n";double f1 = 1.200;cout << f1 << ":\n";cout << (f1 + 1.0 / 9.0) << ":\n";double f2 = 1.67E2;cout << f2 << ":\n";f2 += 1.0 / 9.0;cout << f2 << ":\n";cout << (f2 * 1.0e4) << ":\n";double f3 = 2.3e-4;cout << f3 << ":\n";cout << f3 / 10 << ":\n";return 0; }

    該程序的輸出如下:

    12345678901234567890 K: 273: -273: 1.2: 1.31111: 167: 167.111: 1.67111e+06: 0.00023: 2.3e-05:

    每個值都填充自己的字段。注意,1.200 末尾的 0 沒有顯示出來,但末尾不帶 0 的浮點值后面將有 6 個空格。另外,該實現將指數顯示為 3 位,而其他實現可能為兩位。

  • 修改顯示時使用的計數系統
    ostream 類是從 ios 類派生而來的,而后者是從 ios_base 類派生而來的。ios_base 類存儲了描述格式狀態的信息。例如,一個類成員中某些位決定了使用的計數系統,而另一個成員則決定了字段寬度。通過使用控制符(manipulator),可以控制顯示整數時使用的計數系統。通過使用 ios_base 的成員函數,可以控制字段寬度和小數位數。由于 ios_base 類是 ostream 的間接基類,因此可以將其方法用于 ostream 對象(或子代),如 cout。

    注意:ios_base 類中的成員和方法以前位于 ios 類中。現在,ios_base 是 ios 的基類。在新系統中,ios 是包含 char 和 wchar_t 具體化的模板,而 ios_base 包含了非模板特性。

    來看如何設置顯示整數時使用的計數系統。要控制整數以十進制、十六進制還是八進制顯示,可以使用 dec、hex 和 oct 控制符。例如,下面的函數調用將 cout 對象的計數系統格式狀態設置為十六進制:

    hex(cout);

    完成上述設置后,程序將以十六進制形式打印整數值,直到將格式狀態設置位其他選項為止。注意,控制符不是成員函數,因此不必通過對象來調用。

    雖然控制符實際上是函數,但它們通常的使用方式為:

    cout << hex;

    ostream 類重載了 << 運算符,這使得上述用法與函數調用 hex(cout) 等價??刂品挥诿Q空間 std 中。下面的程序演示了這些控制符的用法,它以3種不同的計數系統顯示了一個整數的值及其平方。注意,可以單獨使用控制符,也可將其作為一系列插入的組成部分。

    // manip.cpp -- using format manipulators #include<iostream> int main(){using namespace std;cout << "Enter an integer: ";int n;cin >> n;cout << "n n*n\n";cout << n << " " << n * n << " (decimal)\n";//set to hex modecout << hex;cout << n << " ";cout << n * n << " (hexadecimal)\n";//set to octal modecout << oct << n << " " << n*n << " (octal)\n";// alternative way to call a manipulatordec(cout);cout << n << " " << n * n << " (decimal)\n";return 0; }

    該程序的運行情況:

    Enter an integer: 8 n n*n 8 64 (decimal) 8 40 (hexadecimal) 10 100 (octal) 8 64 (decimal)
  • 調整字段寬度
    您可能已經注意到,在上面的程序的輸出中各列并沒有對齊,這是因為數字的字段寬度不相同??梢允褂?width 成員函數將長度不同的數字放到寬度相同的字段中,該方法的原型為:

    int width(); int width(int i);

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

    width() 方法只影響將顯示的下一個項目,然后字段寬度將恢復為默認值。例如,請看下面的語句:

    cout << '#'; cout.width(12); cout << 12 << "#" << 24 << "#\n";

    由于 width() 是成員函數,因此必須使用對象(這里為 cout)來調用它。輸出語句生成的輸出如下:

    # 12#24#

    12 被放到寬度為 12 個字符的字段的最右邊,這被稱為右對齊。然后,字段寬度恢復為默認值,并將兩個#符號以及24放在寬度與它們的長度相等的字段中。

    警告:width() 方法只影響接下來顯示的一個項目,然后字段寬度將恢復為默認值。

    C++ 永遠不會截短數據,因此如果試圖在寬度為2的字段中打印一個7位值,C++將增寬字段,以容納該數據(在有些語言中,如果數據長度與字段寬度不匹配,將用星號填充字段。C/C++ 的原則是:顯示所有的數據比保持列的整潔更重要。C++ 視內容重于形式)。下面的程序演示了 width() 成員函數是如何工作的。

    // width.cpp -- using the width method #include<iostream>int main(){using std::cout;int w = cout.width(30);cout << "default field width = " << w << ":\n";cout.width(5);cout << "N" << ':';cout.width(8);cout << "N * N" << ":\n";for (long i = 1; i <= 100; i *= 10) {cout.width(5);cout << i << ':';cout.width(8);cout << i * i << ":\n";}return 0; }

    程序的輸出如下:

    default field width = 0:N: N * N:1: 1:10: 100:100: 10000:

    在上述輸出中,值在字段中右對齊。輸出中包含空格,也就是說,cout 通過加入空格來填滿整個字段。右對齊時,空格被插入到值的左側。用來填充的字符叫做填充字符(fill character)。右對齊是默認的。

    注意,在上面的程序中,第一條 cout 語句顯示字符串時,字段寬度被設置為30,但在顯示 w 的值時,字段寬度不是 30。這是由于 width() 方法只影響接下來被顯示的一個項目。另外,w 的值為0。這是由于 cout.width(30) 返回的是以前的字段寬度,而不是剛設置的值。w 為 0 表明,默認的字段寬度為0。由于 C++ 總會增長字段,以容納數據,因此這種值適用于所有的數據。最后,程序使用 width() 來對齊列標題和數據,方法是將第1列寬度設置為5個字符,將第2列的寬度設置為8個字符。

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

    cout.fill('*');

    這對于檢查打印結果,防止接收方添加數字很有用。下面的程序演示了該成員函數的用法。

    // fill.cpp -- changing fill character for fields #include<iostream>int main() {using std::cout;cout.fill('*');const char * staff[2] = { "Waldo Whipsnade", "Willmarie Wooper" };long bonus[2] = {900, 1350};for( int i = 0; i < 2; i++){cout << staff[i] << ": $";cout.width(7);cout << bonus[i] << "\n";}return 0; }

    下面是程序的輸出:

    Waldo Whipsnade: $****900 Willmarie Wooper: $***1350

    注意,與字段寬度不同的是,新的填充字符將一直有效,直到更改它為止。

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

    cout.precision(2);

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

    // precise.cpp -- setting the precision #include<iostream>int main(){using std::cout;float price1 = 20.40;float price2 = 1.9 + 8.0 / 9.0;cout << "\"Furry Friends\" is $" << price1 << "!\n";cout << "\"Fiery Friends\" is $" << price2 << "!\n";cout.precision(2);cout << "\"Furry Friends\" is $" << price1 << "!\n";cout << "\"Fiery Fiends\" is $" << price2 << "!\n";return 0; }

    下面是程序的輸出:

    "Furry Friends" is $20.4! "Fiery Friends" is $2.78889! "Furry Friends" is $20! "Fiery Fiends" is $2.8!

    注意:第3行沒有打印小數點及其后面的內容。另外,第4行顯式的總位數為2位。

  • 打印末尾的 0 和小數點
    對于有些輸出(如價格或欄中的數字),保留末尾的0將更為美觀。例如,對于上面的程序$20.40 將比 $20.4 更美觀。iostream 系列類沒有提供專門用于完成這項任務的函數,但 ios_base 類提供了一個 setf() 函數(用于 set 標記),能夠控制多種格式化特性。這個類還定義了多個常量,可用作該函數的參數。例如,下面的函數調用使 cout 顯示末尾小數點:

    cout.setf(ios_base::showpoint);

    使用默認的浮點格式時,上述語句還將導致末尾的0被顯示出來。也就是說,如果使用默認精度(6位)時,cout 不會將 2.00 顯示為 2, 而是將它顯示為 2.000000。下面的程序添加了這條語句。

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

    // showpt.cpp -- setting the precision, showing trailing point #include<iostream>int main(){using std::cout;using std::ios_base;float price1 = 20.40;float price2 = 1.9 + 8.0 / 9.0;cout.setf(ios_base::showpoint);cout << "\"Furry Friends\" is $" << price1 << "!\n";cout << "\"Fiery Friends\" is $" << price2 << "!\n";cout.precision(2);cout << "\"Furry Friends\" is $" << price1 << "!\n";cout << "\"Fiery Friends\" is $" << price2 << "!\n";return 0; }

    下面是使用當前 C++ 格式時,程序的輸出:

    "Furry Friends" is $20.4000! "Fiery Friends" is $2.78889! "Furry Friends" is $20.! "Fiery Friends" is $2.8!

    在上述輸出中,第一行顯示了末尾的0,第三行顯示了小數點,但沒有顯示末尾的0,這是因為精度被設置為2,而小數點前面已經包含兩位。

  • 再談 setf()
    setf() 方法控制了小數點被顯示時其他幾個格式選項,因此來仔細研究一下它。ios_base 類有一個受保護的數據成員,其中的各位(這里叫作標記)分別控制著格式化的各個方面,例如計數系統、是否顯示末尾的0等。打開一個標記稱為設置標記(或位),并意味著相應的位被設置為1.位標記是編程開關,相當于設置 DIP 開關以配置計算機硬件。例如,hex、dec 和 oct 控制符調整控制計數系統的 3 個標記位。setf() 函數提供了另一種調整標記位的途徑。

    setf() 函數有兩個原型。第一個為:

    fmtflags setf(fmtflags);

    其中,fmtflags 是 bitmask類型的 typedef 名,用于存儲格式標記。該名稱是在 ios_base 類中定義的。這個版本的 setf() 用來設置單個位控制的格式信息。參數是一個 fmtflags 值,指出要設置哪一位。返回值是類型為 fmtlfags 的數字,指出所有標記以前的設置。如果打算以后恢復原始設置,則可以保存這個值。應給 setf() 傳遞什么呢?如果要第11位設置為1,則可以傳遞一個第11位為1的數字。返回值的第11位將被設置為1。對位進行跟蹤好像單調乏味(實際上也是這樣)。然而,您不必做這這項工作,ios_base 類定義了代表位值的常量,下表列出了其中的一些定義。

    常量含義
    ios_base::boolalpha輸入和輸出bool值,可以為 true 或 false
    ios_base::showbase對于輸出,使用C++基數前綴(0,0x)
    ios_base::showpoint顯示末尾的小數點
    ios_base::uppercase對于16 進制輸出,使用大寫字母,E 表示法
    ios_base::showpos在正數前面加上 +

    注意:bitmask 類型是一種用來存儲各個位值的類型。它可以是整型、枚舉,也可以是 STL bitset 容器。這里的主要思想是,每一位都是可以單獨訪問的,都有自己的含義。iostream 軟件包使用 bitmask 來存儲狀態信息。

    由于這些格式常量都是在 ios_base 類中定義的,因此使用它們時,必須加上作用域解析運算符。也就是說,應使用 ios_base::uppercase,而不是 uppercase。如果不想使用 using 編譯指令或 using 聲明,可以使用作用域運算符來指出這些名稱位于名稱空間 std 中。修改將一直有效,直到被覆蓋為止。下面的程序演示了如何使用其中一些常量。

    // setf.cpp -- using setf() to control formatting #include <ios> #include<iostream>int main(){using std::cout;using std::endl;using std::ios_base;int temperature = 63;cout << "Today's water temperature: ";cout.setf(ios_base::showpos); // show plus signcout << temperature << endl;cout << "For our programming friends, that's\n";cout << std::hex << temperature << endl; // use hexcout.setf(ios_base::uppercase); // use uppercase in hexcout.setf(ios_base::showbase); // use 0X prefix for hexcout << "or\n";cout << temperature << endl;cout << "How " << true << "! oops -- How ";cout.setf(ios_base::boolalpha);cout << true << "!\n";return 0; }

    下面是該程序的輸出:

    Today's water temperature: +63 For our programming friends, that's 3f or 0X3F How 0X1! oops -- How true!

    注意,僅當基數為10時才使用加號。C++將十六進制和八進制都視為無符號的,因此對它們,無需使用符號(然而,有些 C++ 實現可能仍然會顯示加號)。

    第二個 setf() 原型接受兩個參數,并返回以前的設置:

    fmtflags setf(fmtflags, fmtflags);

    函數的這種重載格式用于設置由多位控制的格式選項。第一參數和以前一樣,也是一個包含了所需設置的 fmtflags 值。第二參數指出要清除第一個參數中的哪些位。例如,將第3位設置為1表示以10為基數,將第4位設置為1表示以8為基數,將第5位設置為1表示以16為基數。假設輸出是以10為基數的,而要將它設置為以16為基數,則不僅需要將第5位設置為1,還需要將第3位設置為0——這叫作清除位(clearing the bit)。聰明的十六進制控制符可自動完成這兩項任務。使用函數 setf() 時,要做的工作多些,因為要用第二參數指出要清除哪些位,用第一參數指出要設置哪些位。這并不像聽上去那么復雜,因為 ios_base 位此定義了常量。具體地說,要修改基數,可以將常量 ios_base::basefield 用作第二參數,將 ios_base::hex 用作第一參數。也就是說,下面的函數調用與使用十六進制控制符的作用相同:

    cout.setf(ios_base::hex, ios_base::basefield);

    setf(long, long) 的參數

    第二個參數第一個參數含義
    ios_base::basefieldios_base::dec使用基數10
    ios_base::basefieldios_base::oct使用基數 8
    ios_base::basefieldios_base::hex使用基數 16
    ios_base::floatfieldios_base::fixed使用定點計數法
    ios_base::floatfieldios_base::scientific使用科學計數法
    ios_base::adjustfieldios_base::left使用左對齊
    ios_base::adjustfieldios_base::right使用右對齊
    ios_base::adjustfieldios_base::internal符號或基數前綴左對齊,值右對齊

    ios_base 類定義了可按這種方式處理的 3 組格式標記。每組標記都由一個可用作第二參數的常量和兩三個可用作第一參數的常量組成。第二參數清除一批相關的位,然后第一參數將其中一位設置位1。上表列出了用作 setf() 的第二參數的常量的名稱、可用作第一參數的相關常量以及它們的含義。例如,要選擇左對齊,可將 ios_bass::adjustfield 用作第二參數,將 ios_base::left 作為第一參數。左對齊意味著將值放在字段的左端,右對齊則表示將值放在字段的右端。內部對齊表示將符號或基數前綴放在字段左側,余下的數字放在字段的右側(遺憾的是,C++沒有提供自對齊模式)。

    定點表示法意味著使用格式 123.4 來表示浮點值,而不管數字的長度如何,科學表示法則意味著使用格式 1.23e04,而不考慮數字的長度。如果您熟悉C語言中 printf() 的說明符,則可能知道,默認的 C++ 模式對應于 %g 說明符,定點表示法對應于 %f 說明符,而科學表示法對應于 %e 說明符。

    在 C++ 標準中,定點表示法和科學表示法都有下面的兩個特征:

    • 精度指的是小數位數,而不是總位數;
    • 顯示末尾的0.

    setf() 函數是 ios_base 類的一個成員函數。由于這個類是 ostream 類的基類,因此可以使用 cout 對象來調用該函數。例如,要左對齊,可使用下面的調用:

    ios_base::fmtflags old = cout.setf(ios::left, ios::adjustfield);

    要恢復以前的設置,可以這樣做:

    cout.setf(old,ios::adjustfield);

    下面的程序是一個使用兩個參數的 setf() 的示例。
    注意:下面的程序使用了一個數學函數,有些 C++ 系統不自動搜索數學庫。例如,有些 UNIX 系統要求這樣做:

    $ CC setf2.C -lm

    -lm 選項命令鏈接程序搜索數學庫。同樣,有些使用 g++ 的 Linux 系統也要求這樣做。

    // setf2.cpp -- using setf() with 2 arguments to control formatting #include<iostream> #include<cmath>int main(){using namespace std;// use left justification, show the plus sign, show trailing// zeros, with a precision of 3cout.setf(ios_base::left, ios_base::adjustfield);cout.setf(ios_base::showpos);cout.setf(ios_base::showpoint);cout.precision(3);// use e-notation and save old format settingios_base::fmtflags old = cout.setf(ios_base::scientific, ios_base::floatfield);cout << "Left Justification:\n";long n;for (n = 1; n <= 41; n += 10){cout.width(4);cout << n << "|";cout.width(12);cout << sqrt(double(n)) << "|\n";}// change to internal justificationcout.setf(ios_base::internal, ios_base::adjustfield);// restore default floating-point display stylecout.setf(old, ios_base::floatfield);cout << "Internal Justification:\n";for (n=1; n<=41; n+=10){cout.width(4);cout << n << "|";cout.width(12);cout << sqrt(double(n)) << "|\n";}// use right justification, fixed notationcout.setf(ios_base::right, ios_base::adjustfield);cout.setf(ios_base::fixed, ios_base::floatfield);cout << "Right Justification:\n";for(n=1; n<=41; n+=10) {cout.width(4);cout << n << "|";cout.width(12);cout << sqrt(double(n)) << "|\n";}return 0; }

    下面是程序的輸出:

    Left Justification: +1 |+1.000e+00 | +11 |+3.317e+00 | +21 |+4.583e+00 | +31 |+5.568e+00 | +41 |+6.403e+00 | Internal Justification: + 1|+ 1.00| + 11|+ 3.32| + 21|+ 4.58| + 31|+ 5.57| + 41|+ 6.40| Right Justification:+1| +1.000|+11| +3.317|+21| +4.583|+31| +5.568|+41| +6.403|

    注意到精度 3 讓默認的浮點顯示(在這個程序中用于內部對齊)總共顯示 3 位,而定點模式和科學模式只顯示3位小數(e表示法的指數位數取決于實現)。

    調用 setf() 的效果可以通過 unsetf() 消除,后者的原型如下:

    void unsetf(fmtflags mask);

    其中,mask 是位模式。mask 中所有的位都設置為1,將使得對應的位被復位。也就是說,setf() 將位設置為1,unsetf() 將位恢復為0。例如:

    cout.setf(ios_base::showpoint); // show trailing decimal point cout.unsetf(ios_base::showpoint); // don't show trailing decimal point cout.setf(ios_base::boolalpha); // display true, false cout.unsetf(ios_base::boolalpha); // display 1, 0

    您可能注意到了,沒有專門指示浮點數默認顯示模式的標記。系統的工作原理如下;僅當只有定點位被設置時使用定點表示法;僅當只有科學位被設置時使用科學表示法;對于其他組合,如沒有位被設置或兩位都被設置時,將使用默認模式。因此,啟用默認模式的方法之一如下:

    cout.setf(0, ios_base::floatfield); // go to default mode

    第二個參數關閉這兩位,而第一個參數不設置任何位。一種實現同樣目標的簡捷方式是,使用參數 ios::floatfield 來調用函數 unsetf():

    cout.unsetf(ios_base::floatfield); // go to default mode

    如果已知 cout 處于定點狀態,則可以使用參數 ios_base::fixed 調用函數 unsetf() 來切換到默認模式;然而,無論 cout 的當前狀態如何,使用參數 ios_base::floatfield 調用函數 unsetf() 都將切換到默認模式,因此這是一種更好的選擇。

  • 標準控制符

    使用 setf() 不是進行格式化、對用戶最為友好的方法,C++ 提供了多個控制符,能夠調用 setf(),并自動提供正確的參數。前面已經介紹過 dec、hex 和 oct,這些控制符(多數都不適用于老式 C++ 實現)的工作方式都與 hex 相似。例如,下面的語句打開左對齊和定點選項:

    cout << left << fixed;

    下表列出了這些控制符以及其他一些控制符:
    一些標準控制符

    控制符調用
    boolalphasetf(ios_base::boolalpha)
    noboolalphaunsetf(ios_base::boolalpha)
    showbasesetf(ios_base::showbase)
    noshowbaseunsetf(ios_base::showbase)
    showpointsetf(ios_base::showpoint)
    noshowpointunsetf(ios_base::showpoint)
    showpossetf(ios_base::showpos)
    noshowposunsetf(ios_base::showpos)
    uppercasesetf(ios_base::uppercase)
    nouppercaseunsetf(ios_base::uppercase)
    internalsetf(ios_base::internal, ios_base::adjustfield
    leftsetf(ios_base::left, ios_base::adjustfield
    rightsetf(ios_base::right, ios_base::adjustfield
    decsetf(ios_base::dec, ios_base::basefield
    hexsetf(ios_base::hex, ios_base::basefield
    octsetf(ios_base::oct, ios_base::basefield
    fixedsetf(ios_base::fixed, ios_base::floatfield
    scientificsetf(ios_base::scientific::fixed, ios_base::floatfield

    提示:如果系統支持這些控制符,請使用它們;否則,仍然可以使用 setf()。

  • 頭文件 iomanip
    使用 iostream 工具來設置一些格式值(如字段寬度)不太方便。為簡化工作,C++ 在頭文件 iomanip 中提供了其他一些控制符,它們能夠提供前面討論過的服務,但表示起來更方便。3 個最常用的控制符分別是 setprecision()、setfill() 和 setw(),它們分別用來設置精度、填充字符和字段寬度。與前面討論的控制符不同的是,這3個控制符帶參數。setprecision() 控制符接受一個指定精度的整數參數;setfill() 控制符接受一個指定填充字符的 char 參數;setw() 控制符接受一個指定字段寬度的整數參數。由于它們都是控制符,因此可以用 cout 語句連接起來。這樣 setw() 控制符在顯示多列值時尤其方便。下面的程序演示了這一點,它對于每一行輸出,都多次修改了字段寬度和填充字符,同時使用了一些較新的標準控制符。

    注意:有些 C++ 系統不自動搜索數學庫。前面說過,有些 UNIX 系統要求使用如下命令選項來訪問數學庫:

    $ CC iomanip.C -lm // iomanip.cpp -- using manipulators from iomanip // some systems require explicitly linking the math library#include <iostream> #include <iomanip> #include <cmath>int main() {using namespace std;// use new standard manipulatorscout << fixed << right;// use iomanip munipulatorscout << setw(6) << "N" << setw(14) << "square root"<< setw(15) << "fourth root\n";double root;for (int n = 10; n <= 100; n += 10) {root = sqrt(double(n));cout << setw(6) << setfill('.') << n << setfill(' ')<< setw(12) << setprecision(3) << root<< setw(14) << setprecision(4) << sqrt(root)<< endl;}return 0; }

    下面時該程序的輸出:

    N square root fourth root ....10 3.162 1.7783 ....20 4.472 2.1147 ....30 5.477 2.3403 ....40 6.325 2.5149 ....50 7.071 2.6591 ....60 7.746 2.7832 ....70 8.367 2.8925 ....80 8.944 2.9907 ....90 9.487 3.0801 ...100 10.000 3.1623

    現在可以生成幾乎完全對齊的列了。使用 fixed 控制符導致顯示末尾的0.

  • 總結

    以上是生活随笔為你收集整理的《C++ Primer Plus》第17章:输入、输出和文件(2)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。