2.3.4 迴圈和文字輸入
技術標籤:基礎學習 - C++c++
文章目錄
迴圈和文字輸入
1 使用原始的cin
進行輸入
如果程式要使用迴圈來讀取來自鍵盤的文字輸入,則必須有辦法知道何時停止讀取。
一種方法是選擇某個特殊字元——有時被稱為哨兵字元(sentincl character),將其作為停止標記。
遇到#
字元時停止讀取輸入。該程式計算讀取的字元數,並回顯這些字元,即在螢幕上顯示讀取的字元。
#include <iostream>
int main()
{
using namespace std;
char ch;
int count = 0; // 使用基礎輸入
cout << "輸入字元,輸入 # 退出:\n";
cin >> ch; // 獲取輸入的字元
while (ch != '#')
{
cout << ch;
++count;
cin >> ch;
}
cout << endl << count << "個字元" ;
return 0;
}
- 請注意該程式的結構。該程式在迴圈之前讀取第一個輸入字元,這樣迴圈可以測試第一個字元。這很重要,因為第一個字元可能是
#
。由於使用的是入口條件迴圈,因此在這種情況下,能夠正確地跳過整個迴圈。由於前面已經將變數count
設定為0,因此count
的值也是正確的。 - 如果讀取的第一個字元不是
#
,則程式進入該迴圈,顯示字元,增加計數,然後讀取下一個字元。最後一步是極為重要的,沒有這一步,迴圈將反覆處理第一個輸入字元,一直進行下去。有了這一步後,程式就可以處理到下一個字元。 - 注意,結束迴圈的條件是最後讀取的一個字元是
#
。該條件是通過在迴圈之前讀取一個字元進行初始化的,而通過迴圈體結尾讀取下一個字元進行更新。 - 為啥程式在輸出時省略了空格呢?原因在
cin
。讀取char
值時,與讀取其他基本型別一樣,cin
將忽略空格和換行符。因此輸入中的空格沒有被回顯,也沒有被包括在計數內。 - 更為複雜的是,傳送給
cin
的輸入被緩衝。這意味著只有在使用者按下回車鍵後,他輸入的內容才會被髮送給程式。這就是在執行該程式時,可以在#
後面輸入字元的原因。按下回車鍵後,整個字元序列將被髮送給程式,但程式在遇到#
字元後將結束對輸入的處理。
2 使用cin.get(char)
通常,逐個字元讀取輸入的程式需要檢查每個字元,包括空格、製表符和換行符。
cin
所屬的iostream
類(在iostream
中定義)中包含一個能夠滿足這種要求的成員函式。具體地說,成員函式cin.get(ch)
讀取輸入中的下一個字元(即使它是空格),並將其賦給變數ch
使用這個函式呼叫替換cin>>ch
#include <iostream>
int main()
{
using namespace std;
char ch;
int count = 0; // 使用基礎輸入
cout << "輸入字元,輸入 # 退出:\n";
cin.get(ch); // 獲取輸入的字元
while (ch != '#')
{
cout << ch;
++count;
cin.get(ch);
}
cout << endl << count << "個字元";
return 0;
}
- 現在,該程式回顯了每個字元,並將全部字元計算在內,其中包括空格。輸入仍被緩衝,因此輸入的字元個數仍可能比最終到達程式的要多。
- 標頭檔案
iostream
將cin.get(ch)
的引數宣告為引用型別,因此該函式可以修改其引數的值。
const int ArSize = 100;
char name[ArSize];
cin.get(name, ArSize).get();
相當於兩個函式呼叫:cin.get(name, ArSize);
、cin.get();
cin.get()
的一個版本接受兩個引數:
陣列名(字串(char*型別)的地址)和ArSize(int型別的整數)。
陣列名是其第一個元素的地址,因此字元陣列名的型別為char*
接下來,程式使用了不接受任何引數的cin.get()
。
char ch;
cin.get(ch);
這裡cin.get
接受一個char
引數。
3 檔案尾條件
使用諸如#
等符號來表示輸入結束,這樣的符號可能就是合法輸入的組成部分,其他符號(如@
和%
)也如此。
如果輸入來自於檔案,則可以使用一種功能更強大的技術檢測檔案尾 (EOF)。 C++輸入工具和作業系統協同工作,來檢測檔案尾並將這種資訊告知程式。
如果程式設計環境能夠檢測EOF,可以在程式中使用重定向的檔案,也可以使用鍵盤輸入,並在鍵盤輸入中模擬EOF
檢測到EOF後,cin
將兩位(eofbit
和failbit
)都設定為1
。
可以通過成員函式eof()
來檢視eofbit
是否被設定
如果檢測到EOF,則cin.eof()
將返回bool
值true
,否則返回false
.
同樣,如果eofbit
或failbit
被設定為1
,則fail()
成員函式返回true
,否則返回false
注意,eof()
和fail()
方法報告最近讀取的結果;也就是說,它們在事後報告,而不是預先報告。因此應將cin.eof()
或cin.fail()
測試放在讀取後
fail()
和eof()
,前者可用於更多的實現中。
cin.clear()
來重置輸入流
#include <iostream>
int main()
{
using namespace std;
char ch;
int count = 0;
cout << "輸入字元:\n";
cin.get(ch);
// while (cin.eof() == false) // eof也可以
while (cin.fail() == false) // EOF測試
{
cout << ch;
++count;
cin.get(ch);
}
cout << endl << count << "個字元";
return 0;
}
- Windows 系統上執行該程式,可以按下Ctrl+Z和回車鍵來模擬EOF條件。
- 在Unix和類Unix (包括Linux和Cygwin)系統中,使用者應按Ctrl+Z組合鍵將程式掛起,而命令fg恢復執行程式。
- 通過使用重定向,可以用該程式來顯示文字檔案,並報告它包含的字元數。
3.1 EOF結束輸入
前面指出過,cin
方法檢測到EOF時,將設定cin
物件中一個指示EOF條件的標記。
設定這個標記後,cin
將不讀取輸入,再次呼叫cin
也不管用。
對於檔案輸入,是可行的,因為程式不應讀取超出檔案尾的內容。
然而,對於鍵盤輸入,有可能使用模擬EOF來結束迴圈,但稍後要讀取其他輸入。
cin.clear()
方法可能清除EOF標記,使輸入繼續進行。
在有些系統中,按Ctrl+Z實際上將結束輸入和輸出,而cin.clear()
將無法恢復輸入和輸出。
3.2 常見的字元輸入做法
while (cin.fail() == false)
與while (!cin.fail())
作用相同
方法cin.get(char)
的返回值是一個 cin
物件。
然而,istrearm
類提供了一個可以將istrearm
物件(如cin
)轉換為bool
值的函式
當cin
出現在需要bool
值的地方(如在while
迴圈的測試條件中)時,該轉換函式將被呼叫。
另外,如果最後一次讀取成功了,則轉換得到的bool
值為true
;否則為false
這意味著可以將上述while
測試改寫為這樣:while (cin)
它可以檢測到其他失敗原因,如磁碟故障。
最後,由於cin.get(char)
的返回值為cin
,因此可以將迴圈精簡成這種格式:
while (cin.get(ch))
{
}
#include <iostream>
int main()
{
using namespace std;
char ch;
int count = 0;
cout << "輸入字元:\n";
while (cin.get(ch)) // EOF測試
{
cout << ch;
++count;
}
cout << endl << count << "個字元";
return 0;
}
這樣,cin.get(char)
只被呼叫一次,而不是兩次:迴圈前一次、迴圈結束後一次。
為判斷迴圈測試條件,程式必須首先呼叫cin.get(ch)
。
如果成功,則將值放入ch
中。然後,程式獲得函式呼叫的返回值,即cin
接下來,程式對cin
進行bool
轉換,如果輸入成功,則結果為true
,否則為false
4 C語言中的I/O
C語言中的字元I/O函式:getchar()
和putchar()
包含標頭檔案stdio.h
(或新的cstdio
)即可。也可以使用istream
和ostream
類中類似功能的成員函式
不接受任何引數的cin.get()
成員函式返回輸入中的下一個字元。
ch = cin.get();
該函式的工作方式與C語言中的getchar()
相似,將字元編碼作為int
值返回
而cin.get(ch)
返回一個物件,而不是讀取的字元。
同樣,可以使用cout.put()
函式來顯示字元
cout.put(ch);
該函式的工作方式類似C語言中的putchar()
只不過其引數型別為char,
而不是int
。
最初,put()
成員只有一個原型put(char)
。 可以傳遞一個int
引數給它,該引數將被強制轉換為char
。
C++標準還要求只有一個原型。然而,有些C++實現都提供了3個原型: put(char)
、put(signed char)
和put(unsigned char
)。
在這些實現中,給put()
傳遞一個int
引數將導致錯誤訊息,因為轉換int
的方式不止一種。
使用顯式強制型別轉換的原型(如cin.put(char(ch))
可使用int
引數。
為成功地使用cin.get()
, 需要知道其如何處理EOF條件。當該函式到達EOF時,將沒有可返回的字元。
相反,cin.get()
將返回一個用符號常量EOF表示的特殊值。該常量是在標頭檔案 iostream
中定義的。
EOF值必須不同於任何有效的字元值,以便程式不會將EOF與常規字元混淆。通常,EOF被定義為值-1
,因為沒有ASCII碼為-1
的字元,但並不需要知道實際的值,而只需在程式中使用EOF即可。
#include <iostream>
int main()
{
using namespace std;
int ch; // 可以使用int
int count = 0;
cout << "輸入字元:\n";
ch = cin.get();
while (ch != EOF) // EOF測試
{
cout.put(ch);
++count;
ch = cin.get();
}
cout << endl << count << "個字元";
return 0;
}
如果ch
是一個字元,則迴圈將顯示它。如果ch
為EOF,則迴圈將結束。
需要知道的是,EOF不表示輸入中的字元,而是指出沒有字元。
關於使用cin.get()
還有一個微妙而重要的問題。由於EOF表示的不是有效字元編碼,因此可能不與char
型別相容。
例如,在有些系統中,char
型別是沒有符號的,因此char
變數不可能為EOF值(-1
)。
由於這種原因,如果使用cin.get()
(沒有引數)並測試EOF,則必須將返回值賦給int
變數,而不是char
變數。
另外,如果將ch
的型別宣告為int
,而不是char
,則必須在顯示ch
時將其強制轉換為char
型別。
#include <iostream>
int main()
{
using namespace std;
int ch; // 可以使用int
int count = 0;
cout << "輸入字元:\n";
while ((ch = cin.get()) != EOF) // != 的優先順序大於 = 所以必須加()
{
cout.put(char(ch));
++count;
}
cout << endl << count << "個字元";
return 0;
}
5 cin.get(ch)
與cin.get()
比較
屬性 | cin.get(ch) | ch = cin.get() |
---|---|---|
傳遞輸入字元的方式 | 賦給引數ch | 將函式返回值賦給ch |
用於字元輸入時函式的返回值 | istream 物件(執行bool 轉換為true | int 型別的字元編碼 |
到達EOF時函式的返回值 | istream 物件(執行bool 轉換為false | EOF |
下面的程式碼將輸入中的下一個字元讀入到ch1
中,並將接下來的一個字元讀入到ch2
中:cin.get(ch1).get(ch2);
函式呼叫cin.get(ch1)
返回一個cin
物件,然後便可以通過該物件呼叫get(ch2).
get()
的主要用途是能夠將stdio.h
的getchar()
和putchar()
函式轉換為iostream
的cin.get()
和cout.put()
方法。