????????在C++中,標準輸出流通常指的是與標準輸出設備(通常是終端或控制臺)相關聯的流對象。這個流對象在C++標準庫中被定義為std::cout、std::err、std::clog,它們是std::ostream類的一個實例。
一、cout,cerr和clog流
? ? ? ? ostream類定義了3個輸出流對象,即cout,cerr,clog。
1.1cout流對象
? ? ? ? (1) cout是console output的縮寫,意為控制臺(終端顯示器)的輸出。它不是C++預定義的關鍵字,而是ostream流類的對象,在iostream中定義。顧名思義,流是流動的數據,cout流是流向輸出(顯示設備)的數據,cout流中的數據是用流插入運算符“<<“順序加入的。
? ? ? ? 示例如下:
cout <<"Hello World!" <<"I am learning C++.";
? ? ? ? cout流是容納數據的載體,而并不是運算符。cout將它們輸送到輸出設備上顯示,在顯示設備上輸出”Hello Wordl! I am learning C++.“。
? ? ? ? (2) 在使用"cout <<"輸出基本類型的數據時,不必考慮數據是什么類型,系統會自動判斷數據的類型,并根據類型選擇調用與之匹配的運算符重載函數。示例如下:
cout <<10.0f <<100.58 <<'=' <<"operator";
? ? ? ? (3) cout流在內存中對應開辟了一個緩沖區,用來存儲流中的數據,當向cout流插入一個endl時,不論緩沖區是否已滿,都立即輸出流中所有數據,然后插入一個換行符,并刷新流(清空緩沖區)。示例如下:
cout <<"Hello World" <<endl;
cout <<"I am learning C++" <<endl;
? ? ? ? 注意的是如果插入換行符”\n“,則只是換行而已,并不是刷新cout流,與endl刷新流是清空緩沖區不是一回事。
? ? ? ? (4) 在iostream中只對”<<“和">>"運算符用于標準類型數據的輸入輸出進行了重載,但未對用戶聲明的類型數據的輸入輸出進行重載。所以用戶可以聲明新的類型,并用”<<“和">>"運算符對其進行輸入輸出另作重載。
1.2 cerr流對象
? ? ? ? cerr流對象是標準出錯流,cerr流已被指定為與顯示器關聯,cerr作用是向標準出錯設備(standard error device)輸出有關的出錯信息。cerr是console error的縮寫,意為在控制臺(顯示器)顯示出錯的信息。
? ? ? ? 注意的是cerr和cout的作用和用法雖然差不多,但是有一點不同的是,cout流通常是傳送到顯示設備輸出,也可以被重定向輸出到磁盤文件,而cerr流中的信息只能顯示輸出。
? ? ? ? 通過解一元二次方程,其一般解為
,但若a=0,或
時,用公式出錯。編寫程序,從鍵盤輸入a、b、c的值,求
和
。如果a=0或
,輸出錯誤信息。代碼示例如下:
#include <iostream>
#include <cmath>
using namespace std;int main(){float a, b, c, disc;cout <<"Please enter the values of a, b, c:";cin >>a >>b >>c;// 如果a等于0輸出錯誤信息if(a == 0){cerr <<"a is equal to zero, error!" <<endl;} else{// 如果b * b - 4 * a * c)結果小于0 ,輸出錯誤信息if( (disc = b * b - 4 * a * c) < 0 ){cerr <<"Erro: dist=b*b-4*a*c<0" <<endl;} // 滿足條件,則正常輸出結果else{cout <<"x1 = " <<(-b + sqrt(disc) / (2*a)) <<endl;cout <<"x2 = " <<(-b - sqrt(disc) / (2*a)) <<endl;}}return 0;
}
1)如輸入a=0,運行后結果如下圖:
2)如輸入,運行后結果如下圖:
3)如滿足條件,運行后結果如下圖:
1.3 clog流對象
? ? ? ? clog流對象也是標準出錯流,它是console log的縮寫,作用與cerr相同,都是在終端顯示器上顯示出錯信息。它們之間只是有微小區別。cerr是不經過緩沖區,直接向顯示器上輸出有關信息,而clog中的信息存放在緩沖區中,緩沖區滿后或遇endl時向顯示器輸出。
? ? ? ? 示例如下:
#include <iostream>
using namespace std;int main(){clog <<"This is a message to the C library's error stream." <<endl;clog.flush(); //確保消息被立即刷新到設備return 0;
}
? ? ? ? 運行結果如上圖:
? ? ? ? 實際編程中,cerr通常用于輸出需要立即顯示的錯誤消息或調試信息,而clog用于記錄那些稍后再處理的更詳細的日志信息。這只是常見的約定,并不是強制的。
二、格式輸出
? ? ? ? 在輸出數據時,為簡便起見,往往不指定輸出的格式,由系統根據數據的類型采取默認的格式,但有時希望數據按指定的格式輸出,如要求以十六進制或八進制形式輸出一個整數,對輸出的小數只保留兩位小數等。
2.1 使用控制符控制輸出格式
? ? ? ? 以下為輸出數據的控制符,如下表:
控制符 | 作用 |
---|---|
dec | 設置整數的基數為10 |
hex | 設置整數的基數為16 |
oct | 設置整數的基數為8 |
setbae(n) | 設置整數的基數為n(n只能是8,10,16三者之一) |
setfill(c) | 設置填充字符c,c可以是字符常量或字符變量 |
setprecision(n) | 設置實數的精度為n位,在以一般十進制小數形式輸出時n代表有效數字。在以fixed(固定小數位數)形式和scientific(指數)形式輸出時n為小數位數。 |
setw(n) | 設置字段寬度為n位 |
setiosflags(ios::fixed) | 設置浮點數以固定的小數位數顯示 |
setiosflags(ios::scientific) | 設置浮點數以科技記數法(即指數形式)顯示 |
setiosflags(ios::left) | 輸出數據左對齊 |
setiosflags(ios::right) | 輸出數據右對齊 |
setiosflags(ios::skipws) | 忽略前導的空格 |
setiosflags(ios::uppercase) | 在以科學記數法輸出E和以十六進制輸出字母X時以大寫表示 |
setiosflags(ios::showpos) | 輸出正數時給出”+“號 |
resetioflags() | 終止已設置的輸出格式狀態,在括號中應指定內容 |
? ? ? ? 注意的是,這些控制符是在頭文件iomanip中定義的,因而程序中應當包含頭文件ipmanip。以下通過案例了解以上的方法,代碼如下:
#include <iostream>
#include <iomanip>
using namespace std;int main(){int a = 20; //定義整數a值為20// 輸出信息cout <<"Dec:" <<dec <<a <<endl; //以十進制形式輸出整數cout <<"Hex:" <<hex <<a <<endl; //以十六進制形式輸出整數acout <<"Oct:" <<oct <<a <<endl; //以八進制形式輸出整數acout <<endl;// 通過setbase輸出cout <<"Use setbase to set the base:" <<endl;cout <<"Dec:" <<setbase(10) <<a <<endl; //以十進制形式輸出整數cout <<"Hex:" <<setbase(16) <<a <<endl; //以十六進制形式輸出整數acout <<"Oct:" <<setbase(18) <<a <<endl; //以八進制形式輸出整數acout <<endl;// 設置精度,寬度以及填充字符double PI = 3.1415926;cout <<"set precision 2: " <<setprecision(2) <<PI <<endl; //改為2位小數cout <<setw(10) <<setprecision(2) <<PI <<endl; //設置寬度為10cout <<setfill('*') <<setw(10) <<setprecision(2) <<PI <<endl; //設置填充字符cout <<endl;// setiosfloagscout <<setiosflags(ios::scientific) <<setprecision(8); // 按指數形式輸出8位小數cout <<"PI = " <<PI <<endl; // 指數形式輸出PI值cout <<"precision 4 PI = " <<setprecision(4) <<PI <<endl; // 指數形式 輸出PI為4位小數cout <<"Left aligned, PI = " <<left <<setw(20) <<PI <<endl; // 左側齊cout <<"Right aligned, PI = " <<right <<setw(20) <<PI <<endl; // 右對齊 // 使用大寫字母cout <<"Uppercase: " <<uppercase <<hex <<255 <<endl; //以16進制顯示并使用大寫字母cout <<"Show positive sign:" <<showpos <<PI <<endl;// 注意此時cout <<"Fixed, PI = " <<setiosflags(ios::fixed) <<PI <<endl; // 改為小數形式輸出return 0;
}
? ? ? ? 運行結果如下圖:
? ? ? ? 最后代碼試圖將科學記數法設置為小數位,但是(cout <<"Fixed, PI = " <<setiosflags(ios::fixed) <<PI <<endl;)輸出結果為+0XC.90FDA6896C25P-2,它可能是由于被setiosflags錯誤的解析導致的,通過resetiosflags重置清理掉前面的科學記數法格式即可。在上述代碼后面追加以下代碼即可:
// 重置指數形式
cout <<"reset ios flags:" <<endl;
cout <<resetiosflags(ios::scientific | ios::showpos);
cout <<"PI = " <<PI <<endl;
? ? ? ? resetiosflags中ios::scientific是清除科學記數法,ios::showpos是清除數值前面”+“號,輸出結果如下圖:
2.2 用流對象的成員函數控制輸出格式
? ? ? ? 除了可以用控制符來控制輸出格式,還可以通過調用流對象cout中用于控制輸出格式的成員函數來控制輸出格式。用于控制輸出格式的常用的成員函數如下表:
流成員函數 | 與之作用相同的控制符 | 作用 |
---|---|---|
precision(n) | setprecision(n) | 設置實數的精度為n位 |
width(n) | setw(n) | 設置字段寬度為n位 |
fill(c) | setfill(c) | 設置填充字符c |
setf() | setiosflags() | 設置輸出格式狀態,括號中應給出格式狀態,內容與控制符setiosflags括號中的內容相同。 |
unsetf() | resetiosflag() | 終止已設置的輸出格式狀態,在括號中應該指定內容 |
? ? ? ? 格式標志在類ios中被定義為枚舉值,因此在引用這些格式標志時要在前面加上類名ios和域運算符"::"。格式標志如下表:
格式標志 | 作用 |
---|---|
ios::left | 輸出數據在本域范圍內向左對齊 |
ios::right | 輸出數據在本域范圍內向右對齊 |
ios::internal | 數值的符號位在域寬度內左對齊,數值右對齊,中間由填充字符填充 |
ios::dec | 設置整數的基數為10 |
ios::oct | 設置整數的基數為8 |
ios::hex | 設置整數的基數為16 |
ios::showbase | 強制輸出整數的基數(八進制數以0打頭,十六進制數以0x打頭) |
ios::showpoint | 強制輸出浮點數的小點和尾數0 |
ios::uppercase | 在以科學記數法格式E和以十六進制輸出字母時以大寫表示 |
ios::showpos | 對正數顯示”+“號 |
ios::scientific | 浮點數以科學記數法格式輸出 |
ios::fixed | 浮點數以定點格式(小數形式)輸出 |
ios::unitbuf | 每次輸出之后刷新所有的流 |
ios::stdio | 每次輸出之后清除stdout, stderr |
? ? ? ? 下面使用流控制成員輸出之前案例數據,代碼如下:
#include <iostream>
#include <iomanip>
using namespace std;int main(){int a = 20; //定義整數a值為20// 輸出信息cout.setf(ios::dec); //設置以十進制形式輸出整數cout <<"Dec:" <<a <<endl;cout.unsetf(ios::dec); cout.setf(ios::hex); //設置以十六進制形式輸出整數acout <<"Hex:" <<a <<endl; cout.unsetf(ios::hex);cout.setf(ios::oct); //設置以八進制形式輸出整數acout <<"Oct:" <<a <<endl; cout.unsetf(ios::oct);cout <<endl;// 設置精度,寬度以及填充字符double PI = 3.1415926;cout <<"set precision 2, PI = " <<PI <<endl; //顯示PI值cout.width(30); //設置寬度為30cout <<"set width, PI =" <<PI <<endl; cout.width(30); //設置寬度為30cout.fill('*'); // 置填充字符*cout <<"set fill, PI =" <<PI <<endl;cout <<endl;cout <<"set internal:" <<endl;cout.width(30); //設置寬度為30cout.fill(' '); // 置填充字符*cout.setf(ios::internal | ios::showpos);cout <<PI <<endl;cout.unsetf(ios::internal | ios::showpos);cout <<endl;// 標記為科學記數法cout.setf(ios::scientific); // 設置為科學記數法cout <<"set scientific, PI = " <<PI <<endl;cout.precision(4); //保留4位小數cout <<"precision 4, PI = " <<PI <<endl;cout <<endl;// 指定用定點形式輸出cout.setf(ios::fixed);cout <<"Fixed, PI = " <<PI <<endl;cout.unsetf(ios::fixed);return 0;
}
? ? ? ? 運行后結果如下圖:
? ? ? ? 這里同樣,在最后輸出時PI未得到想要結果,這是因為在設置科學記數法格式cout.setf(ios::scientific)未及時清除設置的格式標志導致的,所以添加cout.unsetf(ios::scientific)及時清除以避免影響后續輸出。
? ? ? ? 完整代碼如下:
#include <iostream>
#include <iomanip>
using namespace std;int main(){int a = 20; //定義整數a值為20// 輸出信息cout.setf(ios::dec); //設置以十進制形式輸出整數cout <<"Dec:" <<a <<endl;cout.unsetf(ios::dec); cout.setf(ios::hex); //設置以十六進制形式輸出整數acout <<"Hex:" <<a <<endl; cout.unsetf(ios::hex);cout.setf(ios::oct); //設置以八進制形式輸出整數acout <<"Oct:" <<a <<endl; cout.unsetf(ios::oct);cout <<endl;// 設置精度,寬度以及填充字符double PI = 3.1415926;cout <<"set precision 2, PI = " <<PI <<endl; //顯示PI值cout.width(30); //設置寬度為30cout <<"set width, PI =" <<PI <<endl; cout.width(30); //設置寬度為30cout.fill('*'); // 置填充字符*cout <<"set fill, PI =" <<PI <<endl;cout <<endl;cout <<"set internal:" <<endl;cout.width(30); //設置寬度為30cout.fill(' '); // 置填充字符*cout.setf(ios::internal | ios::showpos);cout <<PI <<endl;cout.unsetf(ios::internal | ios::showpos);cout <<endl;// 標記為科學記數法cout.setf(ios::scientific); // 設置為科學記數法cout <<"set scientific, PI = " <<PI <<endl;cout.precision(4); //保留4位小數cout <<"precision 4, PI = " <<PI <<endl;cout.unsetf(ios::scientific);cout <<endl;// 指定用定點形式輸出cout.setf(ios::fixed);cout <<"Fixed, PI = " <<PI <<endl;cout.unsetf(ios::fixed);return 0;
}
? ? ? ? 輸出結果如下圖:
三、用流成員函數put輸出字符
? ? ? ? 在程序中一般用cout和插入運算符”<<“實現輸出,cout流在內存中有相應的緩沖區。有時有些特殊輸出要求,ostream類還提供了專用于輸出單個字符的成員函數put。
3.1 ASCII碼輸出
????????如下示例代碼:
#include <iostream>
using namespace std;int main(){// ASCII字符碼輸出int arr[] = {71, 79, 79, 68}; // 字母GOOD分別對應ASCII碼71,79,68// 循環輸出單個字符for(int i = 0; i<4; i++) cout.put(arr[i]);cout.put('\n');return 0;
}
? ? ? ? 運行結果如下圖:
3.2 字符串反轉
????????也可使用cout.put()對字符串進行反轉輸出,示例代碼如下:
#include <iostream>
#include <string>
using namespace std;int main(){string message = "Hello World!"; //定義字符串信息const char *str = message.c_str(); //將字符串轉換為char字符數組// 循環輸出字符信息,倒序輸出for(int i = message.length() - 1; i >= 0; i--) cout.put(*(str + i));// 換行cout.put('\n');return 0;
}
? ? ? ? 運行后結果如下圖:
? ? ? ? 字符指針變量str指向第1個字符'H', a+1則是第2個字符'e'的地址,*(a+1)的值就是’e'。指針的相關知識前面已講解過,想了解朋友可以翻看下,地址:C++面向對象程序設計 - 對象指針和this指針_面向對象版本的指針-CSDN博客
3.3 putchar函數
? ? ? ? 除了可以用cout.put函數輸出一個字符外,還可以用putchar函數輸出一個字符。putchar函數是C語言中使用的,在stdio.h頭文件中定義的。所以3.2中示例可以修改為putchar函數,代碼如下:
#include <iostream>
#include <string>
using namespace std;int main(){string message = "Hello World!"; //定義字符串信息const char *str = message.c_str(); //將字符串轉換為char字符數組// 循環輸出字符信息,倒序輸出for(int i = message.length() - 1; i >= 0; i--) putchar(*(str + i));// 換行cout.put('\n');return 0;
}
? ? ? ? 運行后輸出結果與3.2相同。