20.1 怎樣從鍵盤直接讀入字符而不用等 RETURN 鍵?怎樣 防止字符輸入時的回顯?

唉, 在 C 裡沒有一個標準且可移植的方法。在標準中跟本就 沒有提及屏幕和鍵盤的概念, 只有基於字符 ``流" 的簡單輸入輸出。

在某個級別, 與鍵盤的交互輸入一般上都是由系統取得一行的輸入才 提供給需要的程序。這給操作系統提供了一個加入行編輯的機會 (退格、 刪除、消除等), 使得系統地操作具一致性, 而不用每一個程序自己 建立。當用戶對輸入滿意, 並鍵入 RETURN (或等價的鍵)後, 輸入行才被提供 給需要的程序。即使程序中用了讀入單個字符的函數 (例如 getchar()  等), 第一次調用就會等到完成了一整行的輸入才會返回。這時, 可能 有許多字符提供給了程序, 以後的許多調用 (象 getchar() 的函數)  都會馬上返回。

當程序想在一個字符輸入時馬上讀入, 所用的方式途徑就采決於行處理在 輸入流中的位置, 以及如何使之失效。在一些系統下 (例如 MS-DOS, VMS 的某些模態), 程序可以使用一套不同或修改過的操作系統函數 來擾過行輸入模態。在另外一些系統下 (例如 Unix, VMS 的 另一些模態), 操作系統中負責串行輸入的部分 (通常稱為 ``終端驅動") 必須 設置為行輸入關閉的模態, 這樣, 所有以後調用的常用輸入函數  (例如 read(), getchar() 等) 就會立即返回輸入的字符。 最後, 少數的系統 (特別是那些老舊的批處理大型主機) 使用外圍處理器 進行輸入, 只有行處理模式。

因此, 當你需要用到單字符輸入時 (關閉鍵盤回顯也是類似的問題), 你需要 用一個針對所用系統的特定方法, 假如系統提供的話。新聞組 comp.lang.c  討論的問題基本上都是 C 語言中有明確支持的, 一般上你會從針對個別系統的 新聞組以及相對應的常用問題集中得到更好的解答, 例如  comp.unix.questions 或 comp.os.msdos.programmer。 另外要注意, 有些解答即使是對相似系統的變種也不盡相同, 例如 Unix  的不同變種。同時也要記住, 當回答一些針對特定系統的問題時, 你的答案在你 的系統上可以工作並不代表可以在所有人的系統上都工作。

然而, 這類問題被經常的問起, 這裡提供一個對於通常情況的簡略回答。

某些版本的 curses 函數庫包含了 cbreak(), noecho()  和 getch() 函數, 這些函數可以做到你所需的。如果你只是想要 讀入一個簡短的口令而不想回顯的話, 可以試試 getpass()。在 Unix 系統下, 可以用 ioctl() 來控制終端驅動的模式, ``傳統"系統下有 CBREAK 和 RAW 模式, System V 或 POSIX 系統下有 ICANON,  c_cc[VMIN] 和 c_cc[VTIME] 模式, 而 ECHO 模式 在所有系統中都有。必要時, 用函數 system() 和 stty 命令。 更多的信息可以查看所用的系統, 傳統系統下, 查看 <sgtty.h>  和 tty(4), System V 下, 查看 <termio.h>  和 termio(4), POSIX 下, 查看 <termios.h>  和 termios(4)。在 MS-DOS 系統下, 用函數 getch() 或  getche(), 或者相對應的 BIOS 中斷。在 VMS 下, 使用屏幕管理例程 (SMG$), 或 curses 函數庫, 或者低層  $QIO 的 IO$_READVBLK 函數, 以及 IO$M_NOECHO  等其它函數。也可以通過設置 VMS 的終端驅動, 在單字符輸入或  ``通過" 模式間切換。 如果是其它操作系統, 你就要靠自己了。

另外需要說明一點, 簡單的使用 setbuf() 或 setvbuf() 來設置  sdtin 為無緩衝, 通常並不能切換到單字符輸入模式。

如果你在試圖寫一個可移植的程序, 一個比較好的方法是自己定義三套函數:  1) 設置終端驅動或輸入系統進入單字符輸入模式, (如果有必要的話),  2) 取得字符, 3) 程序使用結束後的終端驅動復原。理想上, 也許有一天, 這樣的一組函數可以成為標準的一部分。本常用問題集的擴充版  (參見問題 20.36) 含有一套適用於幾個流行系統的函數。

參見問題 19.2

參考資料: [PCS, Sec. 10 pp. 128-9, Sec. 10.1 pp. 130-1]; [POSIX, Sec. 7]。

翻譯朱群英、孫雲, LaTeX2HTML 編譯 朱群英 (2005-06-23)