EOF on Windows

最近在學習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的字符時,程序會正常結束。這裏可以看到

  1. 不在行首的^Z無法關閉標準輸入,否則我們應該看到1234而不是上面的輸出
  2. ^Z也會被當做一個普通字符
  3. ^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^ZASCII碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章