cin.peek():C++檢視輸入流中的下一個字元

2020-07-16 10:05:23
peek() 是 istream 類的成員函數,它的原型是:

int peek();

此函數返回輸入流中的下一個字元,但是並不將該字元從輸入流中取走——相當於只是看了一眼下一個字元,因此叫 peek。

cin.peek() 不會跳過輸入流中的空格、回車符。在輸入流已經結束的情況下,cin.peek() 返回 EOF。

在輸入資料的格式不同,需要預先判斷格式再決定如何輸入時,peek() 就能起到作用。

例題:編寫一個日期格式轉換程式,輸入若干個日期,每行一個,要求全部轉換為“mm-dd-yyyy”格式輸出。輸入的日期格式可以是“2011.12.24”(中式格式),也可以是“Dec 24 2011”(西式格式)。要求該程式對於以下輸入資料:

Dec 3 1990
2011.2.3
458.12.1
Nov 4 1998
Feb 12 2011

輸出結果應為:

12-03-1990
02-03-2011
12-01-0458
11-04-1998
02-12-2011

輸入資料中的 Ctrl+Z 略去不寫,因為輸入資料也可能來自於檔案。

編寫這個程式時,如果輸入的是中式格式,就用 cin>>year(假設 year 是 int 型別變數)讀取年份,然後再讀取後面的內容;如果輸入是西式格式,就用 cin>>sMonth(假設 sMonth 是 string 型別物件)讀取月份,然後讀取後面的內容。

可是,如果沒有將資料從輸入流中讀取出來,就無法判斷輸入到底是哪種格式。即便用 cin.get() 讀取一個字元後再作判斷,也很不方便。例如,在輸入為2011.12.24的情況下,讀取第一個字元2後就知道是格式一,問題是輸入流中的已經被讀取了,剩下的表示年份的部分只有011,如何將這個011和前面讀取的2奏成一個整數 2011,也是頗費周折的事情。使用 peek() 函數很容易解決這個問題。

範例程式如下:
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
string Months[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug", "Sep","Oct","Nov","Dec" };
int main()
{
    int c;
    while((c = cin.peek()) != EOF) { //取輸入流中的第一個字元進行檢視
         int year,month,day;
         if(c >= 'A' && c <= 'Z') { //美國日期格式
            string sMonth;
            cin >> sMonth >> day >> year;
            for(int i = 0;i < 12; ++i)  //查詢月份
                   if(sMonth == Months[i]) {
                    month = i + 1;
                    break;
                }
        }
        else { //中國日期格式
            cin >> year ;
            cin.ignore() >> month ; //用ignore跳過 "2011.12.3"中的'.'
            cin.ignore() >> day;
        }
        cin.ignore();   //跳過行末 'n'
        cout<< setfill('0') << setw(2) << month ;//設定填充字元'',輸出寬度2
        cout << "-" << setw(2) << day << "-" << setw(4) << year << endl;
    }
    return 0;
}
istream 還有一個成員函數 istream & putback(char c),可以將一個字元插入輸入流的最前面。對於上面的例題,也可以在用 get() 函數讀取一個字元並判斷是中式格式還是西式格式時,將剛剛讀取的字元再用 putback() 成員函數放回流中,然後再根據判斷結果進行不同方式的讀入。