天天看點

Windows 上 cin>> 與 cin.getline() 混用的問題

經常看到類似下例的問題:

int main()
{
	char buf1[5]={0};
	char buf2[5]={0};
	
	cin >>buf1;
	cin.getline(buf2,5); // 這裡似乎不等待輸入

	return 0;
}
           

而解決辦法如下:

int main()
{
	char buf1[5]={0};
	char buf2[5]={0};
	
	cin >>buf1;
	cin.ignore(); // 或者 cin.sync(); 之類的
	cin.getline(buf2,5);

	return 0;
}
           

這是為什麼呢?

因為,首先, Windows 上敲一下Enter鍵,實質上是輸入兩個字元:回車符,緊跟着換行符。這兩個字元的 ASCII 碼分别為 0x0D 和 0x0A,一般來說,其C++轉義表示分别為 '\r' 和 '\n'。然後,cin>> 預設是以一個或多個接連的白空格為間隔,cin.getline 預設則以單個換行符(0x0A)為間隔。回車符和換行符都屬于白空格。

這裡不讨論為什麼 Windows 上敲Enter鍵會輸入這兩個字元,以及這兩個字元本來應該對應什麼動作(如果存在“本來應該”這麼一說的話)。

為了展示這個看不見的Enter鍵敲擊,用 istringstream 舉個例子:

void disp(char *buf,int n)
{
	for(int i=0;i<n;++i)
		printf("0x%02X ",buf[i]);
	printf("\n");
}

int main()
{
	disp("\r\ns1\r\ns2",9);
	// 這相當于在控制台敲Enter鍵,然後敲入s1,然後敲Enter鍵,然後敲入s2
	// 0x0D 0x0A 0x73 0x31 0x0D 0x0A 0x73 0x32 0x00
	printf("\n");

	{
		istringstream iss("\r\ns1\r\ns2");
		char buf1[5]={0};
		char buf2[5]={0};
		
		iss >>buf1;
		iss >>buf2;
		
		disp(buf1,5); // 0x73 0x31 0x00 0x00 0x00
		disp(buf2,5); // 0x73 0x32 0x00 0x00 0x00
	}

	printf("\n");

	{
		istringstream iss("\r\ns1\r\ns2");
		char buf1[5]={0};
		char buf2[5]={0};
		
		iss.getline(buf1,5); 
		iss.getline(buf2,5);

		disp(buf1,5); // 0x0D 0x00 0x00 0x00 0x00
		disp(buf2,5); // 0x73 0x31 0x0D 0x00 0x00
	}

	return 0;
}
           

用 cin 的道理是一樣的。差別在于相關的标準庫函數會把 0x0D 轉換為 0x0A,這就相當于Enter鍵最終敲入緩沖區的是接連的兩個換行符。由于這不影響讨論,下文還是用 0x0D 指代。

cin>> 每讀到其所期待的東西後碰到 0x0D,就“斷”一下,緊跟着的 0x0A 還在緩沖裡。此時,如果改用 cin.getline() ,0x0A 立即被讀入,而該間隔符前面沒有字元,于是就有了 getline 已完成卻沒有 get 到 line 的錯覺。而如果沒有改用 cin.getline(),繼續用cin>>,那麼 cin>> 碰到緊跟着的 0x0A 時,這是在還沒讀到其所期待的東西就碰到了白空格,它的反應就是跳過該字元(這正是 cin.ignore(); 要做的),接着繼續去讀其所期待的東西。

繼續閱讀