1. 程式人生 > 其它 >2.3.4 迴圈和文字輸入

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;
}

在這裡插入圖片描述

  • 現在,該程式回顯了每個字元,並將全部字元計算在內,其中包括空格。輸入仍被緩衝,因此輸入的字元個數仍可能比最終到達程式的要多。
  • 標頭檔案iostreamcin.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 將兩位(eofbitfailbit)都設定為1

可以通過成員函式eof()來檢視eofbit是否被設定

如果檢測到EOF,則cin.eof()將返回booltrue,否則返回false.

同樣,如果eofbitfailbit被設定為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)即可。也可以使用istreamostream類中類似功能的成員函式

不接受任何引數的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轉換為trueint型別的字元編碼
到達EOF時函式的返回值istream物件(執行bool轉換為falseEOF

下面的程式碼將輸入中的下一個字元讀入到ch1中,並將接下來的一個字元讀入到ch2中:cin.get(ch1).get(ch2);

函式呼叫cin.get(ch1)返回一個cin物件,然後便可以通過該物件呼叫get(ch2).

get()的主要用途是能夠將stdio.hgetchar()putchar()函式轉換為iostreamcin.get()cout.put()方法。