## 1.5 控制臺交互(Communication through console)
控制臺(console)是電腦的最基本交互接口,通常包括鍵盤(keyboard)和屏幕(screen)。鍵盤通常為標準輸入設備,而 屏幕為標準輸出設備。
在C++的iostream函數庫中,一個程序的標準輸入輸出操作依靠兩種數據流:**cin** 給輸入使用和**cout**給輸出使用。另外,**cerr**和**clog**也已經被實現――它們是兩種特殊設計的數據流專門用來顯示出錯信息。它們可以被重新定向到標準輸出設備或到一個日志文件(log file)。
因此cout (標準輸出流)通常被定向到屏幕,**而cin** (標準輸入流)通常被定向到鍵盤。
通過控制這兩種數據流,你可以在程序中與用戶交互,因為你可以在屏幕上顯示輸出并從鍵盤接收用戶的輸入。
### 輸出Output (cout)
輸出流cout與重載(overloaded)運算符<<一起使用:
~~~
cout << "Output sentence"; // 打印Output sentence到屏幕上
cout << 120; // 打印數字 120 到屏幕上
cout << x; // 打印變量 x 的值到屏幕上
~~~
運算符<<又叫插入運算符(insertion operator) 因為它將后面所跟的數據插入到它前面的數據流中。在以上的例子中,字符串常量Output sentence,數字常量120和變量x先后被插入輸出流cout中。注意第一句中字符串常量是被雙引號引起來的。每當我們使用字符串常量的時候,必須用引號把字符串引起來,以便將它和變量名明顯的區分開來。例如,下面兩個語句是不同的:
~~~
cout << "Hello"; // 打印字符串Hello到屏幕上
cout << Hello; // 把變量Hello存儲的內容打印到屏幕上
~~~
插入運算符insertion operator (<<)可以在同一語句中被多次使用:
~~~
cout << "Hello, " << "I am " << "a C++ sentence";
~~~
上面這一行語句將會打印 **Hello, I am a C++ sentence** 到屏幕上。插入運算符(<<) 的重復使用在我們想要打印變量和內容的組合內容或多個變量時有所體現:
~~~
cout << "Hello, I am " << age << " years old and my zipcode is " << zipcode;
~~~
如果我們假設變量age的值為24,變量zipcode的值為90064,以上句子的輸出將為: **Hello, I am 24 years old and my zipcode is 90064**
必須注意,除非我們明確指定,cout并不會自動在其輸出內容的末尾加換行符,因此下面的語句:
~~~
cout << "This is a sentence.";
cout << "This is another sentence.";
~~~
將會有如下內容輸出到屏幕:
**This is a sentence.This is another sentence.**
雖然我們分別調用了兩次**cout**,兩個句子還是被輸出在同一行。所以,為了在輸出中換行,我們必須插入一個換行符來明確表達這一要求。在C++中換行符可以寫作**\n**:
~~~
cout << "First sentence.\n ";
cout << "Second sentence.\nThird sentence.";
~~~
將會產生如下輸出:
~~~
First sentence.
Second sentence.
Third sentence.
~~~
另外,你也可以用操作符endl來換行,例如:
~~~
cout << "First sentence." << endl;
cout << "Second sentence." << endl;
~~~
將會輸出:
~~~
First sentence.
Second sentence.
~~~
當操作符endl被用在buffered streams中時有一點特殊:它們被flushed。不過cout 默認為unbuffered,所以不會被影響。 你可以暫時不管這一點。
你可以使用\n或endl來指定cout輸出換行,請注意前面所講的兩者的不同用法。
### 輸入Input (cin)
C++中的標準輸入是通過在cin數據流上重載運算符extraction (>>) 來實現的。它后面必須跟一個變量以便存儲讀入的數據。例如:
~~~
int age;
cin >> age;
~~~
聲明一個整型變量age然后等待用戶從鍵盤輸入到cin并將輸入值存儲在這個變量中。
**cin** 只能在鍵盤輸入回車鍵(RETURN)后才能處理前面輸入的內容。因此即使你只要求輸入一個單獨的字符,在用戶按下回車鍵(RETURN)之前**cin**將不會處理用戶的輸入的字符。
在使用**cin**輸入的時候必須考慮后面的變量類型。如果你要求輸入一個整數,extraction (>>) 后面必須跟一個整型變量,如果要求一個字符,后面必須跟一個字符型變量,如果要求一個字符串,后面必須跟一個字符串型變量。
~~~
// i/o example
#include <iostream.h>
int main ()
{
int i;
cout << "Please enter an integer value: ";
cin >> i;
cout << "The value you entered is " << i;
cout << " and its double is " << i*2 << ".\n";
return 0;
}
~~~
~~~
Please enter an integer value: 702
The value you entered is 702 and its double is 1404\.
~~~
使用程序的用戶可以使引起錯誤的原因之一,即使是在最簡單的需要用cin做輸入的程序中(就像我們上面看到的這個程序)。因為如果你要求輸入一個整數數值,而用戶輸入了一個名字(一個字符串),其結果可能導致程序產生錯誤操作,因為它不是我們期望從用戶處獲得的數據。當你使用由cin 輸入的數據的時候,你不得不假設程序的用戶將會完全合作而不會在程序要求輸入整數的時候輸入他的名字。后面當我們看到怎樣使用字符串的時候,我們將會同時看到一些解決這一類出錯問題的辦法。
你也可以利用cin 要求用戶輸入多個數據 :
~~~
cin >> a >> b;
~~~
等同于:
~~~
cin >> a;
cin >> b;
~~~
在以上兩種情況下用戶都必須輸入兩個數據,一個給變量a,一個給變量b。輸入時兩個變量之間可以以任何有效的空白符號間隔,包括空格,跳躍符tab或換行。
### cin和字符串
我們可以像讀取基本類型數據一樣,使用cin和>>操作符來讀取字符串,例如:
~~~
cin >> mystring;
~~~
但是,cin >> 只能讀取一個單詞,一旦碰到任何空格,讀取操作就會停止。在很多時候這并不是我們想要的操作,比如我們希望用戶輸入一個英文句子,那么這種方法就無法讀取完整的句子,因為一定會遇到空格。
要一次讀取一整行輸入,需要使用C++的函數 getline,相對于是用cin,我們更建議使用getline來讀取用戶輸入。
例如:
~~~
// 讀取字符串例子
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string mystr;
cout << "What's your name? ";
getline (cin, mystr);
cout << "Hello " << mystr << ".\n";
cout << "What is your favorite color? ";
getline (cin, mystr);
cout << "I like " << mystr << " too!\n";
return 0;
}
~~~
~~~
What's your name? Aqua
Hello Aqua.
What is your favorite color? blue
I like blue too!
~~~
你可能注意到在上面的例子中,兩次調用 getline 函數我們都是用了同一個字符串變量(mystr)。在第二次調用的時候,程序會自動用第二次輸入的內容取代以前的內容。
### 字符串流 (stringstream)
標準頭文件 定義了一個叫做 stringstream 的類,使用這個類可以對基于字符串的對象進行像流(stream)一樣的操作。這樣,我們可以對字符串進行抽取和插入操作,這對將字符串與數值互相轉換非常有用。例如,如果我們想將一個字符串轉換為一個整數,可以這樣寫:
~~~
string mystr ("1204");
int myint;
stringstream(mystr) >> myint;
~~~
這個例子中先定義了一個字符串類型的對象mystr,初始值為"1204",又定義了一個整數變量myint。然后我們使用 stringstream 類的構造函數定義了這個類的對象,并以字符串變量mystr為參數。因為我們可以像使用流一樣使用stringstream 的對象,所以我們可以像使用cin那樣使用操作符 >> 后面跟一個整數變量來進行提取整數數據。這段代碼執行之后變量 myint 存儲的是數值 1204 。
~~~
// 字符串流的使用示例
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main ()
{
string mystr;
float price=0;
int quantity=0;
cout << "Enter price: ";
getline (cin,mystr);
stringstream(mystr) >> price;
cout << "Enter quantity: ";
getline (cin,mystr);
stringstream(mystr) >> quantity;
cout << "Total price: " << price*quantity << endl;
return 0;
}
~~~
~~~
Enter price: 22.25Enter quantity: 7Total price: 155.75
~~~
在這個例子中,我們要求用戶輸入數值,但不同于從標準輸入中直接讀取數值,我們使用函數getline從標注輸入流cin中讀取字符串對象(mystr),然后再從這個字符串對象中提取數值price和quantity。
通過使用這種方法,我們可以對用戶的輸入有更多的控制,因為它將用戶輸入與對輸入的解釋分離,只要求用戶輸入整行的內容,然后再對用戶輸入的內容進行檢驗操作。這種做法在用戶輸入比較集中的程序中是非常推薦使用的。