[英]How to avoid reading weird symbols, by pressing arrow keys with 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-C或CTRL-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
如果 'stty sane' 為您的目的足夠准確地恢復您的設置,您可以節省費用。 '-g' 的格式不能跨 'stty' 版本移植(因此在 Solaris 10 上生成的內容在 Linux 上不起作用,反之亦然),但這個概念在任何地方都適用。 'stty sane' 選項並非普遍可用,AFAIK(但在 Linux 上)。
您可以包含“ncurses”庫,並使用getch()
而不是getchar()
。
“如何避免使用
getchar()
按下Enter 鍵? ”
首先,終端輸入通常是線路或全緩沖。 這意味着操作系統將來自終端的實際輸入存儲到緩沖區中。 通常,當 fe \\n
在stdin
發出信號/提供時,此緩沖區會刷新到程序中。 這是通過按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.