[C++]在 cin 後呼叫 getline 所遇到的問題

在 C++ 中,使用 cin 讀資料後,再用 getline 讀字串,就會遇到 buffer 沒有清空的問題。例如底下的程式碼:


這個例子中,程式先讀一個整數,將之放入 value 變數中。接著讀 value 個字串。所以要是 value 為 2 時,應該就要讀取 2 個字串。但在這個例子中,很奇怪就只會讀 1 個字串。

為什麼??
仔細看底下的輸出,我們會發現 string 1 是一個「空字串」!而 hello world 才是我們真正輸入的那個字串。那麼這個空字串是什麼?



它其實是個 new line,即 \n,或者說是一個 endl。

事情是這樣的。在第 8 行中,我們的程式碼要讀取一個整數:

cin >> value;

然後我們在鍵盤上鍵入 2,然後敲一個 enter。

這個時候,我們的電腦其實得到的是底下的資訊:


緩衝區中有一個 2,還有一個斷行。
那第8行再讀時,只會把 2 讀出來(因為我們要求讀一個整數,而 endl 不是整數,所以不會被讀出來)。

所以最後,緩衝區中會遺留一個 endl 在那兒。於是下次我們呼叫 getline 時,由於 C++ 偵測到緩衝區中還有資料,所以會先把那個 endl 讀出來。這就是為什麼我們只能讀到一個字串的原因。

那怎麼辦?

解決的方法有很多,最常見的有兩個:


  1. 在 cin 後,要是我們知道要呼叫 getline,那就「多呼叫一次 getline」,把緩衝區清空。
  2. 呼叫 cin.ignore() 把緩衝區清空。

這兩種作法都是把緩衝區清空,差別在於方法一需要多宣告一個字串變數。我個人是習慣方法2囉。

底下附上程式碼:

方法1:

方法2:

額外一題,這兒呼叫的 ignore 是一個預設的版本,它的運作方式是這樣的:清空緩衝區,直到底下的兩個條件,其中一個滿足為止!

  1. 清空 1 個字元。
  2. 讀到 endl。
前述兩個條件,只要有一個滿足,那 ignore 就會停止所有的動作了。

條件 2 比較容易想像,本來 ignore 就是要清空緩衝區,直到遇到 endl 為止。條件 1 比較神奇,為什麼只清空 1 個字元?沒法子,這是人家工程師預設好的。就照規定來囉。

要是使用預設的版本時,有時候遇到使用者「手殘」,那就有問題了。舉例如下:


上例中,使用者手殘,多輸入了一個 + (別笑,有時候打字快,總是會發生這種事)。這個時候由於 ignore 只清空一個字元,所以它會把 + 清掉,但是還是把 newline 留著,所以我們的程式還是會讀到那個 newline。

解法是要求 ignore 多清一些字元,程式碼示範如下:

上面的程式碼中,ignore 需要兩個參數,第1個參數要求 ignore 清空 1000 個字元。第2個參數要求 ignore 清空緩衝區,直到遇到 \n (new line)。這兩個要求只要其中之一滿足,ignore 就會停止。

有關 cin 和 getline 的問題,網路上有許多的討論,底下列了一些參考文獻,寫得很清楚,大家可以參考:

stackoverflow 的文章:cin and getline skipping input
ignore 的使用方式

留言

這個網誌中的熱門文章

由 Pandas 的 DataFrame 中取得資料

[程式設計] C++ 的字串切割

[程式設計] UVa 介紹,以及 UDebug 和其他輔助工具的介紹