最近在學習go
時,在運行一個簡單的讀取標準輸入的例子時發現,在控制檯上(cmd
)輸入ctrl-z
竟然不能結束程序。以前在學習c/c++時,也遇到過有關的問題,也就是當我想要結束輸入時,如果在輸入ctrl-z
之前又輸入了某些其他字符,那麼此時標準輸入不會被關閉,而當一行中僅有一個ctrl-z
時,才能結束輸入。下面一個簡單的c++程序可以驗證這個問題:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string line;
while (getline(cin, line)) {
cout << line << endl;
}
}
如果我們運行時輸入
1234^Z5678
此時如果只按一次回車,是看不到輸出的,當我們再按一次回車時,才能看到如下輸出
1234->
這裏->
是輸出的一個箭頭符號代表^Z
,但是當我們輸入一行僅包含^Z
的字符時,程序會正常結束。這裏可以看到
- 不在行首的
^Z
無法關閉標準輸入,否則我們應該看到1234
而不是上面的輸出 ^Z
也會被當做一個普通字符^Z
所在行之後的字符(包括換行)都不會顯示,也就是庫函數會將^Z
之後的該行字符全部丟掉
具體造成這種問題的原因,可以參考All About EOF,文中提到了使用ctrl-z
作爲僞EOF的歷史原因,還提到了使用eof
檢測函數的一些常見錯誤。
*nix
系統的終端默認使用ctrl-d
來結束標準輸入,但是ctrl-d
其實也不是EOF,EOF只是在當標準輸入關閉時,讀取後返回的一個特殊值。ctrl-d
會被終端解釋爲關閉標準輸入的指令。
在windows的console上,我們可以如下檢測^Z
,
package main
import (
"bufio"
"golang.org/x/crypto/ssh/terminal"
"os"
"runtime"
)
func main() {
in := bufio.NewReader(os.Stdin)
for {
r, n, err := in.ReadRune()
if runtime.GOOS == "windows" && terminal.IsTerminal(int(os.Stdin.Fd())) && r == '\x1a' {
// ctrl-z detected
break
}
}
}
這裏檢測程序運行在控制檯上,以防止當重定向輸入時,輸入中包含^Z
的情況。\x1a
是^Z
的ASCII
碼。