使用cin對象對C風格字符串執行輸入操作時存在一個缺陷,如下:
#include <iostream> using namespace std; int main() { const int stringSize = 64; char string1[stringSize]; char string2[stringSize];
// enter 1st string cout << "Enter first string: "; cin >> string1;
// enter 2nd string cout << "Enter second string: "; cin >> string2;
// show the result of input cout << "The first string is " << "\"" << string1 << "\"" << ", the second string is " << "\"" << string2 << "\"" << endl;
// pause
system("pause"); return 0; }
一般情況下該程序應該可以正常完成工作:接收用戶輸入的兩段字符串(有長度限制),並一起進行輸出。
然而當用戶的輸入中包含空格等空白元素時,則會出現下述意料之外的狀況
Enter first string: test string1
Enter second string: The first string is "test", the second string is "string1"
解釋上面的運行情況之前,不妨先考慮一個問題,cin對象是如何確定已完成字符串輸入?由於C風格字符串使用空字符‘0’作為結尾,而這種空字符是無法通過鍵盤之間輸入的,因此cin需要借助別的方法來確定字符串的結尾位置。cin使用空白(空格,制表符,換行符)來確定字符串的結尾位置,這意味着cin在獲取字符數組輸入時只讀取一個單詞(或者說不內含空白的一段字符串)。讀取該單詞后,cin將該字符串放到數組中,並自動在結尾添加空字符。這樣一來也就能很好的解釋上面的情況了。
面向行的輸入
每次讀取一個單詞通常不是最好的選擇,要將整條短語而不是一個單詞作為字符串輸入,需要采用另一種字符串讀取方法。具體的說,需要采用面向行而不是面向單詞的方法。幸運的是,istream中的類提供了一些面向行的類成員函數:getline()和get()。這兩個函數都讀取一行輸入,直到到達換行符。然而,隨后getline()將丟棄換行符,而get()將換行符保留在輸入序列中,下面詳細介紹它們,首先介紹getline()。
- cin.getline()
getline()函數讀取整行,它使用通過回車鍵輸入的換行符來確定輸入結尾。要調用這種方法,可以使用cin.getline()。該函數有兩個參數。第一個參數是用來存儲輸入行的數組的名稱,第二個參數是要讀取的字符數。如果這個參數為20,則函數最多讀取19個字符,余下的空間用於存儲自動在結尾處添加的空字符。getline()成員函數在讀取指定數目的字符或遇到換行符時停止讀取。
#include <iostream> using namespace std; int main() { const int stringSize = 64; char string1[stringSize]; char string2[stringSize]; // enter 1st string cout << "Enter first string: "; //cin >> string1; cin.getline(string1, stringSize); // enter 2nd string cout << "Enter second string: "; //cin >> string2; cin.getline(string2, stringSize); // show the result of input cout << "The first string is " << "\"" << string1 << "\"" << ", the second string is " << "\"" << string2 << "\"" << endl; // pause system("pause"); return 0; }
運行結果:
Enter first string: test string1
Enter second string: test string2
The first string is "test string1", the second string is "test string2"
- cin.get()
我們來試試另一種方法。istream類有另一個名為get()的成員函數,該函數有幾種變體。其中一種變體的工作方法與getline()類似,它們接受的參數相同,解釋參數的方式也相同,並且都讀取到行尾,但get()並不再讀取並丟棄換行符,而是將其留在輸入隊列中。假設我們連續兩次調用get():
cin.get(string1, stringSize);
cin.get(string2, stringSize); // 將會出現問題
由於第一次調用后,換行符將留在輸入隊列中,因此第二次調用時看到的第一個字符便是換行符,因此get()認為已經到達行尾,而沒有發現任何可讀取的內容。如果不借助幫助,get()將不能跨過該換行符。
幸運的是,get()有另一種變體。使用不帶任何參數的cin.get()調用可讀取下一個字符(即使是換行符),因此可以用它來處理換行符,為讀取下一行輸入做好准備。也就是說,可以采用下面的調用序列:
cin.get(string1, stringSize); // 讀取第一行
cin.get(); // 讀取換行符
cin.get(string2, stringSize); // 讀取第二行
另一種使用get()的方式是將兩個類成員函數拼接起來(合並),如下所示:
cin.get(string1, stringSize).get();
之所以可以這樣做,是由於cin.get(string1, stringSize)返回一個cin對象,該對象隨后將被用來調用cin.get()函數。同樣,下面的語句將把輸入中連續的兩行分別讀入到數組string1和string2中,其效果與兩次調用cin.getline()相同:
cin.getline(string1, stringSize).getline(string2, stringSize);
#include <iostream> using namespace std; int main() { const int stringSize = 64; char string1[stringSize]; char string2[stringSize]; // enter 1st string cout << "Enter first string: "; //cin >> string1; cin.get(string1, stringSize).get(); // enter 2nd string cout << "Enter second string: "; //cin >> string2; cin.get(string2, stringSize).get(); // show the result of input cout << "The first string is " << "\"" << string1 << "\"" << ", the second string is " << "\"" << string2 << "\"" << endl; // pause system("pause"); return 0; }
運行結果:
Enter first string: test string1
Enter second string: test string2
The first string is "test string1", the second string is "test string2"
為什么要使用get(),而不是getline()呢?首先,老式實現中並沒有getline()。其次,get()使輸入更仔細。例如,假設用get()將一行讀入數組中。如何知道停止讀取的原因是由於已經讀取了整行,而不是由於數組已經被填滿?查看下一個輸入字符,如果是換行符,說明已讀取了整行;否則,說明該行中還有其他輸入。總之,getline()使用起來簡單一些,但get()使得檢查錯誤更簡單些。可以用其中的任何一個來讀取一行輸入;只是應該知道,它們的行為稍有不同。
