? ? ? ?
目錄
cin原理介紹
控制符(hex、oct、dec)
cin如何檢查輸入
cin與字符串
cin.get(char ch)
cin.get(void)
istream &get(char*,int)
istream &get(char*,int,char)
istream &getline(char*,int);
遇到文件結尾EOF
無法完成一次完整輸入:設置failbit為1
發生硬件錯誤時:badbit被設置為1
設置cin狀態:clear()和setstat()
丟棄后續字符:istream &ignore(int =1,int =EOF)
關鍵總結
? ? ? ? cin什么時候會被設置eofbit和failbit
? ? ? ? cin.get(char)什么時候會被設置eofbit和failbit
????????cin.get(void)什么時候會被設置eofbit和failbit
? ? ? ? cin.get(char*,int)什么時候會被設置eofbit和failbit
????????cin.getline(char*,int)什么時候會被設置eofbit和failbit
????????cin >> str 和 cin.get(char*,int)有什么區別
????????cin.get(char*,int)與cin.getline(char*,int)有什么區別
? ? ? ? cin怎么停的
? ? ? ? cin怎么開始的
? ? ? ? cin.get(char*,int)與cin.getline(char*,int)怎么開始的
char cin.peek()
istream& cin.putback( char ch)
詳細了解cin,了解c++的IO邏輯,對我們的工作以及字符串leetcode題目都大有好處。文章中盡量用更加通俗的語言,去給大家描述。舉了大量的例子,去給大家演示。只要大家能夠跟著文章節奏走,一定會有很大收獲。對于日后的c++文件操作,大有裨益。對于文章中的筆誤之處,歡迎大家批評指正。
cin原理介紹
? ? ? ? 大家感興趣的可以先看一下這篇博文:cout格式控制? ? ? ? ,了解cout和cin有助于我們深切理解c++處理IO的邏輯。
? ? ? ? c++把輸入輸出都看做字節流。通過鍵盤所生成的屏幕上的字符,就是字符字節流或字符序列。
int a;
cin >> a;
? ? ? ? 可是變量a底層存儲的是32個二進制位,4個字節。cin不可能每次嚴格讀取四個字符輸入給a。這就是大家要明白的一件事:抽取時進行了類型轉換--->將字符字節流轉換為相應變量類型的二進制位
- 輸入123
- cin >> a;a是int;此時cin將字符123轉化為4字節二進制,存儲到a。
- cin >> a;a是double;此時cin將字符123轉化為8字節二進制,存儲到a。
控制符(hex、oct、dec)
- cin >> hex;后續cin會持續將輸入解釋為16進制;
- cin >> oct;后續cin會持續將輸入解釋為8進制;
- cin >> dec;后續cin會持續將輸入解釋為10進制
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{int a;cin >> hex;cin >> a;// 此時鍵入12cout << a << endl;cout << hex ;cout << a << endl;return 0;
}
cin如何檢查輸入
- cin會跳過開頭所有空白(空白:空格、制表符、換行符);
- cin在遇到第一個類型不匹配的字符時會停止。例如,cin >> a(int);輸入“? ? 123S”,cin遇到S時會停止
- 注意:cin正在繼續匹配的過程中遇到空白,認為空白是類型不匹配的字符
- 注意:即使是:cin >> ch(char);大家知道char類型當然是可以包括空格字符的,但是即使是這樣cin依然會跳過所有的空白,無法把空格賦給ch。
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{int a;char ch;cin >> a;// 此時鍵入123S--->123會被輸入給a,字符'S'會留在輸入隊列里cout << a << endl;cin >> ch;// 將輸入隊列里的'S'取出,賦給chcout << ch << endl;return 0;
}
cin與字符串
? ? ? ? cin將輸入流中的數據賦給字符串指針時,會自動在后面添加'\0'。正常的字符串中當然可以有空格、制表符、換行符,但是cin同樣會跳過開頭空白,并且認為中間的空白是類型不匹配的字符。
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{char ch[20];cin >> ch;// 輸入“ 123 456"cout << ch << endl;return 0;
}
cin.get(char ch)
????????cin.get(char ch)將下一字符賦給ch,即使下一字符是空格、制表符、換行符。cin.get(char ch)返回指向該cin的引用,因此可以拼接使用:cin.get(char ch1).get(char ch2).get(char ch3)。
? ? ? ? cin.get(char ch)與cin >> ch的比較:
- 兩者都是從輸入隊列里獲取下一個字符
- cin.get(char ch)的的確確是獲取下一個字符,cin >> ch會跳過空白去尋找下一個字符
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{char ch;cin.get(ch);// 輸入“ 1"cout << ch << endl;return 0;
}
cin.get(void)
- 返回類型為 int
- 取出下一個字符,將這個字符的ascII碼返回
- 由于返回類型為int,所以不能拼接使用。例如:cin.get().get(),這樣寫是錯誤的。
????????接下來的例子中,大家運行一下,想想其中的邏輯。
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{int a;a = cin.get();// 輸入1cout << a << endl;a = cin.get();cout << a << endl;a = cin.get();// 輸入2cout << a << endl;return 0;
}
? ? ? ? cin.get(char)與cin.get(void)都是用于處理單字符的。接下來我們會講解四個處理字符串的成員函數。
istream &get(char*,int)
? ? ? ? 假設有:
char line[5];
cin.get(line,5);
? ? ? ? cin.get(char*,int)會讀取4個字符,然后將數組末尾添加'\0';如果cin.get(char*,int)還未讀取4個字符就遇到了換行符,那么cin.get(char*,int)將停止讀取,補上'\0',并且把換行符留在輸入隊列中。
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{char line[5];cin.get(line,5); // 輸入1234\ncout << line << endl;char ch;cin.get(ch); // 此時還殘留在輸入隊列里的'\n'將會被賦給ch,也就是說此處控制臺不會提示讓我們繼續輸入cout << ch;return 0;
}
istream &get(char*,int,char)
? ? ? ? 與istream &get(char*,int)邏輯完全一樣,只不過把默認的遇到換行符停止變為自定義的char而已。
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{char line[5];cin.get(line,5,'$'); // 輸入1234$cout << line << endl;char ch;cin.get(ch); // 此時還殘留在輸入隊列里的'$'將會被賦給ch,也就是說此處控制臺不會提示讓我們繼續輸入cout << ch;return 0;
}
istream &getline(char*,int);
? ? ? ? 同istream &get(char*,int)邏輯一樣,主要區別是:istream &getline(char*,int)會把換行符從輸入隊列中取出,然后丟棄。
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{char line[5];cin.getline(line,5); // 輸入1234\ncout << line << endl;char ch;cin.get(ch); // 此時輸入隊列里為空,也就是說此處控制臺會提示讓我們繼續輸入cout << ch;return 0;
}
istream &getline(char*,int,char);
? ? ? ? 與istream &getline(char*,int)邏輯完全一樣,只不過把默認的遇到換行符停止變為自定義的char而已。
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{char line[5];cin.getline(line,5,'$'); // 輸入1234$\ncout << line << endl;char ch;cin.get(ch); // 此時輸入隊列里還有一個'\n',也就是說此處控制臺不會提示讓我們繼續輸入cout << ch;return 0;
}
遇到文件結尾EOF
? ? ? ? EOF是個宏,值是-1,不同系統中可能數值不一樣;什么叫做文件結尾,換行符首先不是文件結尾,換行符是一個字符,這一點要先搞清楚。當我們想要讀取文件時,文件最后一個字符的后面會被插入一個EOF,這個EOF就是文件結尾。在控制臺上我們可以通過快捷鍵模擬出文件結尾:window--->ctrl+z;linux/macos--->ctrl+d。
? ? ? ? 回顧一下我們都講了幾種輸入方式:
- cin
- cin.get(char)
- cin.get(void)
- cin.get(char*,int)
- cin.getline(char*,int)
? ? ? ? 這幾種輸入,一旦遇到文件結尾。都會使對象cin變為錯誤狀態。
? ? ? ? cin對象內部有一個比特位,這個比特位對應的變量(這里我們權且把它叫做比特位變量吧,大家都能理解),叫做eofbit。很顯然eofbit取值只能是0和1。默認情況下,eofbit = 0,代表cin狀態正常。上文講的所有輸入方式一旦遇到文件結尾,就會把cin對象內部的eofbit比特級變量設置為1,此時cin處于錯誤狀態。這意味著:cin >> ch;cin.get(char);cin.get(char*,int);cin.getline(char*,int)全部都會將eofbit設置為1。這還意味著接下來的cin不能正常使用。
????????cin.eof()用于判斷eofbit是否被設置為1,被設為1返回true,否則返回false;
? ? ? ? 在說明這件事之前,有一個小細節需要表明。cin >> ch等,平時返回cin對象。如果放在if中,即if(cin >> ch)將會返回布爾值。如果完成一次完整輸入,則返回false,否則將返回false。
? ? ? ? 何時無法完成一次完整輸入呢
- cin狀態一開始就處在錯誤狀態
- cin處在良好狀態,讀到的第一個非空字符就不符合要求
? ? ? ? 何時完成一次完整輸入呢
- cin讀取到正確字符并正常返回,此時cin狀態良好
- cin讀取到了正確字符,繼續讀時遇到eof,此時cin處在錯誤狀態。但是完成了一次完整輸入,if(cin >> a)成立。
? ? ? ? 關于if(cin >> a)何時成立,大家按照上述邏輯自行測試。下面是測試eofbit的代碼。
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{char line[5];cin >> line; //輸入12ctrl+d 我在vscode下需要兩次ctrl+dif(cin.eof()){cout << "eofbit == 1"<< endl;//這意味著確實讀到了文件結尾EOF}else{cout << "eofbit == 0"<< endl;}if(cin >> line){cout << "cin is true" << endl;}else{cout << "cin is false" << endl;}return 0;
}
無法完成一次完整輸入:設置failbit為1
? ? ? ? failbit與上述eofbit邏輯一樣,區別是eofbit在遇到文件結尾EOF時被設置為1,而failbit在無法完成一次完整輸入被設置為1。cin.eof()用于檢測eofbit是否被設置為1,而cin.fail()用于檢測failbit是否被設置為1。failbit被設置為1時,cin同樣處于錯誤狀態
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{int a;cin >> a; //輸入S if(cin.fail()){cout << "failbit == 1"<< endl;//這意味著讀取失敗,failbit被設置}else{cout << "failbit == 0"<< endl;}if(cin >> a){cout << "cin is true" << endl;}else{cout << "cin is false" << endl;}return 0;
}
發生硬件錯誤時:badbit被設置為1
? ? ? ? 發生硬件錯誤時,badbit被設置為1,此時cin處在錯誤狀態。cin.bad()用于檢測badbit是否為1。不再贅述。
設置cin狀態:clear()和setstat()
? ? ? ? eofbit、failbit、badbit只要有一個被設為1,cin就會處于錯誤狀態。處于錯誤狀態的cin將無法使用。
- cin.clear():清除全部3個狀態位
- cin.clear(eofbit):設置eofbit為1,其余兩個狀態位被清除
- setstat(eofbit):設置eofbit為1,不對另外兩個狀態做任何操作
? ? 別著急測試,等講完下一個再測試,后續會說明原因。
丟棄后續字符:istream &ignore(int =1,int =EOF)
? ? ? ? cin.ignore(255,'\n'):丟棄后續255個字符或者遇到換行符。
? ? ? ? 默認cin.ignore(int =1,int =EOF)丟棄指定數目的字符或者遇到文件結尾。
? ? ? ? 大家多加體會一下下列代碼
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{int a;cin >> a; //輸入S if(cin.fail()){cout << "failbit == 1"<< endl;//failbit被設置,cin進入錯誤狀態}else{cout << "failbit == 0"<< endl;}cin.clear(); // 設置cin為正常狀態,但是不匹配字符還在輸入隊列里cin.ignore(1,'\n');if(cin >> a) // 此時cin狀態恢復正常,輸入隊列里已經沒有數據,此時控制臺會要求再次輸入數據{cout << "cin is true" << endl;}else{cout << "cin is false" << endl;}return 0;
}
關鍵總結
? ? ? ? 我們已經幾乎全部講完cin相關知識,為了能夠更加牢靠的記住這些知識。我們做一些大致的總結和比較,也就是說我們簡單想一些場景,進行總結比較一下。如果大家覺得有什么問題或者錯誤,感謝大家評論區批評指正。對于初學者別怕麻煩,最好一個個測試一下。
? ? ? ? cin什么時候會被設置eofbit和failbit
- cin >> ch(char);除非第一個非空字符就是EOF文件結尾,此時會同時設置eofbit和failbit;其余情況,不可能設置eofbit或者failbit(我說的不可能,是因為我確實沒有想到相關場景。如果說成大概率不會設置...,這樣我說的話更不容易出錯。可是這種說辭很不利于我們學習成長,這里不是在考試。我們應當渴望自己發現問題,并改正它)。
- cin >> str(char*);第一個字符就遇到EOF,會同時設置eofbit和failbit;已經讀取了一些字符,繼續讀取時遇到EOF,此時會設置eofbit;
- cin >> a(int):第一個字符就遇到EOF,第一個非空字符不是數字,都會同時設置eofbit和failbit;已經讀取了相關數字,繼續讀取時遇到EOF,此時會設置eofbit;
? ? ? ? cin.get(char)什么時候會被設置eofbit和failbit
- 除非第一個非空字符就是EOF文件結尾,此時會同時設置eofbit和failbit;其余情況,不可能設置eofbit或者failbit。
????????cin.get(void)什么時候會被設置eofbit和failbit
- 遇到文件結尾時cin.get()返回EOF;除非第一個非空字符就是EOF文件結尾,此時會同時設置eofbit和failbit。
? ? ? ? cin.get(char*,int)什么時候會被設置eofbit和failbit
- 第一個非空字符就是EOF文件結尾,此時會同時設置eofbit和failbit;此時會把空值'\0'放入數組中
- 已經讀取了一些字符,繼續讀取時遇到EOF,此時會設置eofbit;
- 直接讀取到換行符,此時會設置failbit,因為沒有讀取成功啊。
????????cin.getline(char*,int)什么時候會被設置eofbit和failbit
- 第一個非空字符就是EOF文件結尾,此時會同時設置eofbit和failbit;此時會把空值'\0'放入數組中
- 已經讀取了一些字符,繼續讀取時遇到EOF,此時會設置eofbit;
- 直接讀取到換行符,不會設置failbit,因為getline從輸入隊列中取走了換行符只不過丟棄了它
- cin.getline(str,30):如果已經讀取了29個字符,下一個字符不是換行符,將設置failbit。因此包含30個或更多字符的輸入行將終止輸入。
????????cin >> str 和 cin.get(char*,int)有什么區別
? ? ? ? 這個問題留給大家,主要是相互類比加深印象,已經進一步理解其特性。
????????cin.get(char*,int)與cin.getline(char*,int)有什么區別
- 前者遇到換行符停止,換行符留在輸入隊列;后者遇到換行符終止,換行符被取走丟棄
- 當第一個字符就是換行符時,前者屬于無法完成一次完整輸入,設置failbit;后者讀取丟棄這個換行符,不設置failbit;兩者都把空白加入str
- 后者讀取字符數到達num-1后,如果下一個字符不是換行符,將設置failbit;前者則不會
? ? ? ? cin怎么停的
- 遇到第一個不匹配的字符停止
- 注意:cin正在繼續匹配的過程中遇到空白,認為空白是類型不匹配的字符。即使是cin >> str(char*),它依然視空白為不匹配的字符。
? ? ? ? cin怎么開始的
? ? ? ? 這個問題比較微妙,大家后續學習中大概率會有些疑問,到時候大家讀一下下面這一句話。看看我總結的對不對,歡迎批評指正。
- cin在輸入隊列里找數據,它丟棄一個個空白,只要輸入隊列里沒有要匹配的數據,控制臺就打開輸入,讓你輸入數據。
? ? ? ? cin.get(char*,int)與cin.getline(char*,int)怎么開始的
- 如果輸入隊列沒有數據,則從調用處開始,從控制臺獲得數據
- 如果輸入隊列里有數據,則讀取這個輸入隊列里的數據。對于控制臺IO來講,它不可能在從控制臺獲得數據。
? ? ? ? 至此文章主要邏輯已經全部闡述完畢,剩下的篇幅,用來給大家講兩個好用的cin成員方法。
char cin.peek()
- 返回輸入隊列中的下一個字符,但是不抽取這個字符,只是看看而已
- 大家自行測試即可
istream& cin.putback( char ch)
- 將一個字符插入到輸入字符串中,被插入的字符將是下一條輸入語句讀取的第一個字符
- 相當于cin.peek()與cin.get(char)組合使用
#include <iostream>int main()
{using std::cout;using std::cin;using std::endl;char ch;while(cin.get(ch)) {if (ch != '#')cout << ch;else{cin.putback(ch); break;}}if (!cin.eof()){cin.get(ch);cout << endl << ch << " is next input character.\n";}else{cout << "End of file reached.\n";std::exit(0);}while(cin.peek() != '#') {cin.get(ch);cout << ch;}if (!cin.eof()){cin.get(ch);cout << endl << ch << " is next input character.\n";}elsecout << "End of file reached.\n";return 0;
}
? ? ? ? 有什么問題大家可以在評論區說明,歡迎大家批評指正。