簡體   English   中英

如何避免按 Enter 和 getchar() 只讀取單個字符?

[英]How to avoid pressing Enter with getchar() for reading a single character only?

在下一個代碼中:

#include <stdio.h>

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}

我必須按Enter才能打印我用getchar輸入的所有字母,但我不想這樣做,我想做的是按下字母並立即看到我重復輸入的字母而不按Enter 例如,如果我按下字母“a”,我想在它旁邊看到另一個“a”,依此類推:

aabbccddeeff.....

但是當我按'a'時沒有任何反應,我可以寫其他字母並且只有當我按Enter時才會出現副本:

abcdef
abcdef

我怎樣才能做到這一點?

我在 Ubuntu 下使用命令cc -o example example.c進行編譯。

這取決於您的操作系統,如果您處於類似 UNIX 的環境中,則 ICANON 標志默認啟用,因此輸入將被緩沖,直到下一個'\\n'EOF 通過禁用規范模式,您將立即獲得字符。 這在其他平台上也是可能的,但沒有直接的跨平台解決方案。

編輯:我看到您指定使用 Ubuntu。 我昨天剛剛發布了類似的內容,但請注意,這將禁用終端的許多默認行為。

#include<stdio.h>
#include <termios.h>            //termios, TCSANOW, ECHO, ICANON
#include <unistd.h>     //STDIN_FILENO


int main(void){   
    int c;   
    static struct termios oldt, newt;

    /*tcgetattr gets the parameters of the current terminal
    STDIN_FILENO will tell tcgetattr that it should write the settings
    of stdin to oldt*/
    tcgetattr( STDIN_FILENO, &oldt);
    /*now the settings will be copied*/
    newt = oldt;

    /*ICANON normally takes care that one line at a time will be processed
    that means it will return if it sees a "\n" or an EOF or an EOL*/
    newt.c_lflag &= ~(ICANON);          

    /*Those new settings will be set to STDIN
    TCSANOW tells tcsetattr to change attributes immediately. */
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);

    /*This is your part:
    I choose 'e' to end input. Notice that EOF is also turned off
    in the non-canonical mode*/
    while((c=getchar())!= 'e')      
        putchar(c);                 

    /*restore the old settings*/
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);


    return 0;
}

您會注意到,每個字符都出現了兩次。 這是因為輸入會立即回顯到終端,然后您的程序也將其與putchar()一起放回。 如果您想將輸入與輸出分離,您還必須關閉 ECHO 標志。 您可以通過簡單地將適當的行更改為:

newt.c_lflag &= ~(ICANON | ECHO); 

在 linux 系統上,您可以使用stty命令修改終端行為。 默認情況下,終端將緩沖所有信息,直到按下Enter鍵,甚至在將其發送到 C 程序之前。

一個快速、骯臟且不是特別便攜的示例,用於更改程序本身的行為:

#include<stdio.h>
#include<stdlib.h>

int main(void){
  int c;
  /* use system call to make terminal send all keystrokes directly to stdin */
  system ("/bin/stty raw");
  while((c=getchar())!= '.') {
    /* type a period to break out of the loop, since CTRL-D won't work raw */
    putchar(c);
  }
  /* use system call to set terminal behaviour to more normal behaviour */
  system ("/bin/stty cooked");
  return 0;
}

請注意,這並不是最佳選擇,因為它只是假設stty cooked是程序退出時您想要的行為,而不是檢查原始終端設置是什么。 此外,由於在原始模式下會跳過所有特殊處理,因此許多鍵序列(例如CTRL-CCTRL-D )實際上不會像您期望的那樣工作,而無需在程序中顯式處理它們。

您可以man stty以更好地控制終端行為,具體取決於您想要實現的目標。

getchar() 是一個標准函數,在許多平台上都需要您按 ENTER 來獲取輸入,因為平台會緩沖輸入,直到按下該鍵為止。 許多編譯器/平台支持不關心 ENTER 的非標准 getch()(繞過平台緩沖,將 ENTER 視為另一個鍵)。

I/O 是一種操作系統功能。 在許多情況下,操作系統不會將鍵入的字符傳遞給程序,直到按下 ENTER。 這允許用戶在將輸入發送到程序之前修改輸入(例如退格和重新鍵入)。 對於大多數目的,這很有效,為用戶提供了一致的界面,並使程序不必處理這個問題。 在某些情況下,程序希望在按鍵被按下時從鍵中獲取字符。

C 庫本身處理文件,並不關心數據如何進入輸入文件。 因此,語言本身無法在按鍵被按下時獲取它們; 相反,這是特定於平台的。 由於您尚未指定操作系統或編譯器,我們無法為您查找。

此外,標准輸出通常被緩沖以提高效率。 這是由 C 庫完成的,因此有一個 C 解決方案,即fflush(stdout); 在寫下每個字符之后。 之后,是否立即顯示字符取決於操作系統,但我熟悉的所有操作系統都會立即顯示輸出,所以這通常不是問題。

我喜歡盧卡斯的回答,但我想詳細說明一下。 termios.h有一個名為cfmakeraw()的內置函數, man將其描述為:

cfmakeraw() sets the terminal to something like the "raw" mode of the
old Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. [...]

這基本上與 Lucas 建議的相同,更多,您可以在手冊頁中看到它設置的確切標志: termios(3)

用例

int c = 0;
static struct termios oldTermios, newTermios;

tcgetattr(STDIN_FILENO, &oldTermios);
newTermios = oldTermios;

cfmakeraw(&newTermios);

tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermios);

switch (c) {
    case 113: // q
        printf("\n\n");
        exit(0);
        break;
    case 105: // i
        printf("insert\n");
        break;
    default:
        break;

由於您正在使用 Unix 衍生產品 (Ubuntu),因此這是一種方法 - 不推薦,但它會起作用(只要您可以准確地鍵入命令):

echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program

當您對程序感到無聊時,使用中斷來停止程序。

sh restore-sanity
  • 'echo' 行將當前終端設置保存為可恢復它們的 shell 腳本。
  • 'stty' 行關閉大部分特殊處理(例如,因此Control-D不起作用)並在字符可用時立即將其發送到程序。 這意味着您不能再編輯您的輸入。
  • 'sh' 行恢復您的原始終端設置。

如果 'stty sane' 為您的目的足夠准確地恢復您的設置,您可以節省費用。 '-g' 的格式不能跨 'stty' 版本移植(因此在 Solaris 10 上生成的內容在 Linux 上不起作用,反之亦然),但這個概念在任何地方都適用。 'stty sane' 選項並非普遍可用,AFAIK(但在 Linux 上)。

您可以包含“ncurses”庫,並使用getch()而不是getchar()

如何避免使用getchar()按下Enter 鍵

首先,終端輸入通常是線路或全緩沖。 這意味着操作系統將來自終端的實際輸入存儲到緩沖區中。 通常,當 fe \\nstdin發出信號/提供時,此緩沖區會刷新到程序中。 這是通過按Enter 鍵完成的

getchar()就在鏈的末尾。 它沒有能力實際影響緩沖過程。


我該怎么做?

首先放棄getchar() ,如果您不想使用特定的系統調用來顯式更改終端的行為,就像其他答案中所解釋的那樣。

不幸的是,沒有標准庫函數,也沒有可移植的方式在單個字符輸入時刷新緩沖區。 但是,存在基於實現的和不可移植的解決方案。


在 Windows/MS-DOS 中, conio.h頭文件中有getch()getche()函數,它們完全符合您的要求——讀取單個字符而無需等待換行符刷新緩沖區.

之間的主要差別getch()getche()getch()不立即輸出在控制台中實際輸入的字符,而getche()一樣。 附加的"e"代表echo

例子:

#include <stdio.h>
#include <conio.h>   

int main (void) 
{
    int c;

    while ((c = getche()) != EOF)
    {
        if (c == '\n')
        {
            break;
        }

        printf("\n");
    }

    return 0;
}

在 Linux 中,獲得直接字符處理和輸出的一種方法是使用 ncurses 庫中的cbreak()echo()選項以及getch()refresh()例程。

請注意,您需要使用initscr()初始化所謂的標准屏幕,並使用endwin()例程關閉相同的屏幕

例子:

#include <stdio.h>
#include <ncurses.h>   

int main (void) 
{
    int c;

    cbreak(); 
    echo();

    initscr();

    while ((c = getch()) != ERR)
    {
        if (c == '\n')
        {
            break;
        }

        printf("\n");
        refresh();
    }

    endwin();

    return 0;
}

注意:您需要使用-lncurses選項調用編譯器,以便鏈接器可以搜索並找到 ncurses-library。

是的,您也可以在 Windows 上執行此操作,這是下面的代碼,使用 conio.h 庫

#include <iostream> //basic input/output
#include <conio.h>  //provides non standard getch() function
using namespace std;

int main()
{  
  cout << "Password: ";  
  string pass;
  while(true)
  {
             char ch = getch();    

             if(ch=='\r'){  //when a carriage return is found [enter] key
             cout << endl << "Your password is: " << pass <<endl; 
             break;
             }

             pass+=ch;             
             cout << "*";
             }
  getch();
  return 0;
}

我在我目前正在處理的作業中遇到了這個問題。 它還取決於您從哪個輸入中抓取。 我在用

/dev/tty

在程序運行時獲取輸入,因此需要是與命令關聯的文件流。

在 ubuntu 機器上我必須測試/定位,它需要的不僅僅是

system( "stty -raw" );

或者

system( "stty -icanon" );

我必須添加 --file 標志,以及命令的路徑,如下所示:

system( "/bin/stty --file=/dev/tty -icanon" );

現在所有的東西都是醋酸。

這段代碼對我有用。 注意:這不是標准庫的一部分,即使大多數編譯器(我使用 GCC)都支持它。

#include <stdio.h>
#include <conio.h>
int main(int argc, char const *argv[]) {
    char a = getch();
    printf("You typed a char with an ASCII value of %d, printable as '%c'\n", a, a);
    return 0;
}

此代碼檢測第一次按鍵。

可以創建一個檢查Enter的新函數:

#include <stdio.h>

char getChar()
{
    printf("Please enter a char:\n");

    char c = getchar();
    if (c == '\n')
    {
        c = getchar();
    }

    return c;
}

int main(int argc, char *argv[])
{
    char ch;
    while ((ch = getChar()) != '.')
    {
        printf("Your char: %c\n", ch);
    }

    return 0;
}

默認情況下,C 庫會緩沖輸出,直到看到返回。 要立即打印結果,請使用fflush

while((c=getchar())!= EOF)      
{
    putchar(c);
    fflush(stdout);
}

您可以使用 _getch() 而不是 <conio.h> 中的 getch()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM