之前我們提到過,在 C 語言中,使用 scanf,printf 等函數實現輸入和輸出,但是在 C++ 中則是採用了 cin,cout以及流操作符進行輸入和輸出操作。
IO 類
但實際上不管是 cin 還是 cout,兩者都是類對象,主要的類的繼承關係爲:
而在 iostream 中已經事先定義了 cin,cout,cerr,clog 等對象,因此能夠在包含頭文件和聲明命名空間之後直接使用。
上邊各個類的主要作用爲:
iostream | istream | 從流中讀取 |
ostream | 寫入到流 | |
iostream | 從流中讀取,寫入到流 | |
fstream | ifstream | 從文件中讀取 |
ofstream | 寫入到文件 | |
fstream | 從文件中讀取,寫入到文件 |
IO 流特性
- 對象不可複製或賦值
- IO 對象是緩衝的,刷緩衝的時機爲:
- 程序正常結束,將清空所有緩衝區
- 緩衝區滿
- flush 會刷緩衝,endl 在刷緩衝後還會換行
- 在每次輸出操作執行完成後,可以使用 unitbuf 操作符設置流的內部狀態,清空緩衝區
<</>>
現在可以知道流對象後可以接 <</>> 是函數重載的結果了。
標準輸出
輸出流對象
- cout:可以輸出基本類型數據,與 printf 相比,不用指定要輸出的數據類型,內部經過重載,因此係統會自動判斷,選擇相應的重載函數。但用戶如果想要輸出自定義的類型時,就需要進行 << 重載
- cerr:能夠在屏幕上輸出錯誤信息,不能將信息輸出到文件
- clog:與 cerr 類似,只是不帶有緩衝區
iomanip
C 語言能夠通過格式控制字符來控制輸出的格式,在 C++ 中則是通過流算子來進行格式控制的:
控制符 | 描述 |
dec | 十進制輸出 |
hex | 十六進制輸出 |
oct | 八進制輸出 |
setfilll(n) | 在給定的輸出域寬度內填充字符n |
setprecison(n) | 設置小數顯示精度爲n |
setw(n) | 設置輸出域寬爲n |
setiosflags(ios::fixed) | 固定的浮點顯示 |
setiosflags(ios::showpos) | 顯示符號 |
setiosflags(ios::scientific) | 指數顯示 |
setiosflags(ios::left) | 左對齊 |
setiosflags(ios::right) | 右對齊 |
setiosflags(ios::skipws) | 忽略前導空白 |
setiosflags(ios::uppercase) | 十六進制大寫輸出 |
resetiosflags(ios::lowercase) | 恢復小寫輸出 |
setiosflags(ios::showbase) |
當按十六進制輸出數據時,前面顯示前導符 0x 當按八進制輸出數據時,前面顯示前導符 0 |
endl | 刷新緩衝區並換行 |
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int x = 500;
cout<<dec<<x<<endl;
cout<<oct<<x<<endl;
cout<<hex<<x<<endl;
cout<<setw(5)<<setfill('*')<<x<<endl;
cout<<dec<<endl;
double y = 1.111111111111111;
cout<<y<<endl;
cout<<setprecision(2)<<y<<endl;
cout<<setw(5)<<y<<endl;
cout<<setiosflags(ios::fixed)<<y<<endl;
cout<<setw(5)<<setiosflags(ios::left)<<x<<endl;
cout<<setw(5)<<setiosflags(ios::right)<<x<<endl;
cout<<dec<<setiosflags(ios::skipws)<<x<<endl;
cout<<hex<<setiosflags(ios::uppercase)<<x<<endl;
cout<<hex<<resetiosflags(ios::uppercase)<<x<<endl;
cout<<hex<<setiosflags(ios::showbase)<<x<<endl;
return 0;
}
結果爲:
500
764
1f4
**1f4
1.11111
1.1
**1.1
1.11
500**
**500
500
1F4
1f4
0x1f4
成員函數
put
typedef basic_ostream<_CharT, _Traits> __ostream_type;
__ostream_type& put(char_type __c);
可以得知,put 函數的參數爲 char,返回值爲流引用。
主要作用就是輸出一個字符,類似於 putchar。
#include <iostream>
using namespace std;
int main()
{
char *p = "hello";
while (*p)
cout.put(*p++);
return 0;
}
結果爲:
hello
標準輸入
cin 是 C++ 中的標準輸入,cin 利用重載的 >> 能夠連續輸入多項內容。和標準輸出一樣,如果需要輸入用戶自定義數據類型,則需要對 >> 進行重載。
#include <iostream>
using namespace std;
int main()
{
int x;
double y;
char z;
cin>>x>>y>>z;
cout<<x<<endl;
cout<<y<<endl;
cout<<z<<endl;
return 0;
}
結果爲:
10 10.2 a
10
10.2
a
輸入字符以空白字符作爲分隔符,空白字符包括空格,TAB,ENTER。
成員函數
使用 cin 時,如果字符串中含有空白字符,對於 cin 來說就有點應付不來了。因此成員函數作爲標準輸入的補充內容,可以應付一些比較特殊的情況。
char get()
int_type get();
和 put() 相反,get() 能夠讀入一個字符(包括空白字符),有點類似於 getchar()。
#include <iostream>
using namespace std;
int main()
{
char c;
c = cin.get();
cout<<c<<endl;
return 0;
}
istream get(char &)
__istream_type& get(__streambuf_type& __sb)
{ return this->get(__sb, this->widen('\n')); }
get() 還能夠有參數,此時讀入一個字符,讀取成功則返回非 0,失敗則返回 0;
#include <iostream>
using namespace std;
int main()
{
char c;
cin.get(c);
cout<<c<<endl;
return 0;
}
istream &get(char *,int,char)
__istream_type& get(char_type* __s, streamsize __n, char_type __delim);
- char *:表示字符指針
- int:表示字符個數
- char:表示終止字符
- 函數表示從輸入流中讀取 n-1 個字符,將讀取到的字符賦給字符指針指向的地址
- 如果在讀取完畢之前提前遇到終止字符,則提前結束讀取;
- 如果成功則返回非 0,失敗則返回 0;
- 會清空 char * 指向的空間,未讀到 n-1 個字符或中止符,則會阻塞
- 不會越過中止符
#include <iostream>
using namespace std;
int main()
{
char p[100];
cin.get(p,10,'o');
cout<<p<<endl;
return 0;
}
istream &getline(char *,int,char)
__istream_type& getline(char_type* __s, streamsize __n, char_type __delim);
與上一個函數的含義差不多。
- char *:表示字符指針
- int:表示字符個數
- char:表示終止字符
- 函數表示從輸入流中讀取 n-1 個字符,將讀取到的字符賦給字符指針指向的地址
- 如果在讀取完畢之前提前遇到終止字符(默認爲 '\n'),則提前結束讀取;
- 如果成功則返回非 0,失敗則返回 0;
- 會清空 char * 指向的空間,未讀到 n-1 個字符或中止符,則會阻塞
- 會越過中止符
該函數與上一個函數的區別就在於對於中止符的處理上,get 在遇到中止符時,會停止當前動作,中止符仍處於流中,再次調用之前需要使用不帶參數的 get() 去除掉該中止符,否則該函數會直接返回,getline 則不會發生這個問題。
#include <iostream>
using namespace std;
int main()
{
char p[100];
cin.get(p,10,'o');
cout<<p<<endl;
cin.get();
cin.get(p,10,'o');
cout<<p<<endl;
cin.getline(p,10,'o');
cout<<p<<endl;
cin.getline(p,10,'o');
cout<<p<<endl;
return 0;
}
結果爲:
hellohellohellohellohellohellohello
hell
hell
hell
上邊的結果表示:
- get 不會越過中止符,因此需要 cin.get() 從當前輸入流中讀走中止符
- 而 getline 顯然沒有這種限制
ignore
忽略流中的 n 個字符,或者讀取到中止符未知(包含中止符),n 默認爲 1
__istream_type& ignore(streamsize __n, int_type __delim);
__istream_type& ignore(streamsize __n);
__istream_type& ignore();
#include <iostream>
using namespace std;
int main()
{
char p[100];
cin.ignore(2);
cin.getline(p,10,'\n');
cout<<p<<endl;
return 0;
}
結果爲:
hello world
llo world
peek
讀取流中的一個字符然後將之放回原位置,指針指向不發生改變。
#include <iostream>
using namespace std;
int main()
{
char p[100];
cin.ignore(1);
cout<<static_cast<char>(cin.peek())<<endl;
cin.getline(p,6,'\n');
cout<<p<<endl;
return 0;
}
結果爲:
hello world
e
ello
putback
插入當前指針位置
#include <iostream>
using namespace std;
int main()
{
char p[100];
cin.putback('l');
cin.getline(p,6,'\n');
cout<<p<<endl;
return 0;
}
結果爲:
hello world
lhell