[C/C++] 輸入比較:cin, get, getline

一、<iostream>

1. cin

(1) 目的

輸入字符(token),字符主要由空白所間隔出的字串,如:"Tsung Square is a good web site."有7個字符,分別為"Tsung"、"Square"、"is"、"a"、"good"、"web"、"site."。

(2) 特色

可多載C字串或C++字串,也就是可以輸入到C風格的內建字元陣列,也可以輸入到C++風格的字串類別。

(3) 空白、換行、tab符號

如果空白 (" ")、換行 ("\n")、tab ("\t")符號出現在前端,cin會在前端連續將他們吃掉捨棄,並繼續連續輸入字元,直到尾端出現這三種符號或EOF (end of file)為止,但cin不會在尾端吃掉他們,也就是他們會保留於輸入流中。如:

  1. 輸入:"\t\n    \ttsung     \t\t square"
  2. str1:"\t\n    \t"丟掉並輸入"tsung"
  3. str2:"     \t\t "丟掉並輸入"square"

(4) 形式

char str1[10];  //C風格的內建字元陣列
string str2;  //C++風格的字串類別
cin >> str1 >> str2;


2. cin.get()

(1) 目的

沒有引數或只有1個引數時,可以輸入字元;如果有2個引數時,可以輸入整行;如果有3個引數時,可以輸入整行或其他輸入的變化。

(2) 特色

和cin.getline()的最大差別在於,尾端如果遇到換行符號(分界符號),cin.get()不會吃掉他,會保留他於輸入流中,只能用於C風格的內建字元陣列。

(3) 換行符號

對於2引數形式,如果前端出現換行符號,cin.get()會吃掉保存1個換行並停止,所以如果前端有2個換行,就會有2個cin.get()分別吃掉保存換行符號;如果尾端出現換行符號,cin.get()會保留換行符號於下次輸入並停止。如:

  1. 輸入"\n\n tsung \t\n square"
  2. str1: "\n" // 前端的換行符號會吃掉保留
  3. str2:"\n"
  4. str3:" tsung \t" // 注意尾端的換行符號cin.get()不會吃掉,而會保留於輸入流中
  5. str4:"\n"
  6. str5:" square"

(4) 形式

字元變數 = cin.get() // 無引數版如需保留字元,需用指定運算子 (=)
cin.get(字元變數)
cin.get(字元陣列 , 陣列大小) // 分界字元預設為換行符號 '\n'
cin.get(字元陣列 , 陣列大小, 分界字元)

3. cin.getline()

(1) 目的

和cin.get()類似,可以輸入整行。

(2) 特色

和cin.get()的最大差別在於,尾端如果遇到換行符號(分界符號),cin.getline()會吃掉捨棄他,只能用於C風格的內建字元陣列。

(3) 換行符號

如果前端出現換行符號,cin.getline()會吃掉捨棄1個換行並停止,所以如果前端有2個換行,就會有2個cin.getline()分別吃掉換行符號;如果尾端出現換行符號,cin.getline()會吃掉捨棄1個換行符號並停止。如:

  1. 輸入"\n\n tsung \t\n square"
  2. str1: "null" // 前端的換行符號會吃掉捨棄
  3. str2:"null"
  4. str3:" tsung \t" // 注意尾端的換行符號被cin.getline()吃掉捨棄一個
  5. str4:" square"

(4) 形式

cin.getline(字元陣列 , 陣列大小)
cin.getline(字元陣列 , 陣列大小 , 分界位元)


二、<string>

1. getline()

(1) 目的

和cin.getline()類似,可以整行輸入。

(2) 特色

和cin.getline()最大差別就是,getline()是屬於<string>類別的,只能接受C++風格的字串類別,而不能接受C風格的內建字元陣列,並且不用指定陣列大小;而cin.getline()則相反。

(3) 換行符號

遇到換行符號的處理和cin.getline()一樣,會吃掉捨棄1個前端換行並停止,或是吃掉捨棄1個後端換行符號並停止。

(4) 形式

getline(cin , C++字串)
getline(cin , C++字串 , 分界位元)


三、<cstdio>:C原有的標頭檔

1. scanf()

(1) 目的

輸入字符。

(2) 特色

和C++的cin很像,但所在的標頭檔不一樣,且sacnf()必須明確告知資料型別,以及無法自行擴充。cin搭串流配擷取運算子 (>>)可以擴充自身,使用多載運算子的方法,可使cin >>輸入自定義的型別。

(3) 空白、換行、tab符號

遇到這三種符號和cin的處理方式一樣,scanf()會在前端連續將他們吃掉捨棄,並繼續連續輸入字元,直到尾端出現這三種符號或EOF (end of file)為止,但scanf()不會在尾端吃掉他們,會保留他們於輸入流中

(4) 形式

scanf("形式字串" ,  相對應的變數們)


2. fgets()

(1) 目的

限制長度輸入整行

(2) 特色

和gets()很像,但比較安全因為可以限制字串長度,且可以設定輸入方式(通常設為 標準輸入stdin)。

(3) 換行符號

如果前端遇到換行符號,會吃掉保留1個換行符號在會放入陣列中並停止;如果輸入長度 > 陣列長度 - 1,吃不到換行符號;輸入長度 = 陣列長度 - 1,不會吃掉;但如果輸入長度 < 陣列長度 - 1,會吃掉保留換行符號。所以fgets()輸出時可能會一同將換行符號一起輸出。如:

  1. 輸入:"\n tsung square\n good\n"
  2. str1:"\n" // 吃掉保留前端'\n'
  3. str2[7]:" tsung" // 輸入長度 > 陣列長度 - 1,吃不到
  4. str3[8]:" square" // 輸入長度 = 陣列長度 - 1,不會吃掉
  5. str4:'\n'
  6. str5[10]:" good\n" // 輸入長度 < 陣列長度 - 1,吃掉保留

(4) 形式

fgets(字元陣列 , 陣列長度 , 輸入方式)


3. gets()

(1) 目的

輸入整行。

(2) 特色

和fgets()很像,沒有長度限制,所以可能會有記憶體溢位 (overflow)的風險。

(3) 換行符號

不建議使用

(4) 形式

gets(字元陣列)


4. getchar()

(1) 目的

輸入字元

(2) 特色

和getc()很像,但可以設定不同的輸入形式,如果輸入形式為stdio,則相等於getc()。

(3) 形式

字元變數 = getchar(輸入形式)


5. getc()

(1) 目的

標準輸入字元

(2) 特色

和getchar()很像,沒有引數所以使用上更為簡潔,getc() == getchar(stdio)

(3) 形式

字元變數 = getc()


四、名詞解釋與統整

  1. 吃掉捨棄:字元從輸入流中遺棄,也不會保留於變數之中。
  2. 吃掉保留: 字元從輸入流保存於變數之中。
  3. 不會吃掉:字元仍在輸入流當中。
  4. 字元 (character):字串的基本單元。
  5. 字符 (token):主要由空白所間隔出的字串。
  6. 字串 (string):由字元所組成的串列。
  7. 字元陣列:C風格的內建指標陣列,宣告方式如:char str[ ]或char *str。
  8. 字串類別:C++風格的標準樣板類別,宣告方式如:string str。
  9. EOF:end of file的縮寫,即為檔案結束。
  10. 陣列長度:上述引數的陣列長度 - 1 = 實際能輸入到陣列中的長度,因為最後一個位子會被填上 '\0',通常 '\0'不會printt到螢幕中。
前端遇到換行符號,只有cin和scanf()吃掉捨棄會繼續輸入,其餘指令不管吃掉捨棄或保存都會停止;而後端要特別注意不會吃掉換行符號的指令 cin , cin.get() , scanf(),由於這樣的特性,使得換行指令還保留在輸入流中,所以當下一個指令如果會因為前端遇到換行指令而停止,這會造成應該輸入的字串沒有被輸入到對應變數中(邏輯錯誤)。解決方法可以在兩指令中安插一個指令吃掉換行符號,如:
  1. cin.get():吃掉保留1個字元
  2. cin.ignore():忽略1個字元
  3. cin.ignore(int n):忽略n個字元

五、參考文獻

  1. GeeksforGeeks。2018。Difference between getc(), getchar(), getch() and getche()。 [Accessed Jan 2021]
  2. ITREAD01。2019。C++中的cin、cin.get()、cin.getline()、getline()、gets()等函式的用法。 [Accessed Jan 2021]

留言