Enter tuşuna basılmasını beklemeden standart girişteki karakterleri yakalayın


174

Bunu nasıl yaptığımı asla hatırlayamıyorum çünkü bu benim için çok seyrek oluyor. Ancak C veya C ++ 'da, bir satırsonu beklemeden standart girişten bir karakter okumanın en iyi yolu nedir (enter tuşuna basın).

Ayrıca ideal olarak giriş karakterini ekrana yansıtmaz. Sadece konsol ekranını etkilemeden tuş vuruşlarını yakalamak istiyorum.


1
@adam - Açıklığa kavuşturabilir misiniz: Hiçbir karakter yoksa hemen dönecek bir işlev mi yoksa her zaman tek bir tuşa basmayı bekleyecek bir işlev mi istiyorsunuz?
Roddy

2
@Roddy - Her zaman tek bir tuşa basmayı bekleyecek bir işlev istiyorum.
Adam

Yanıtlar:


99

Bu, saf C ++ 'da taşınabilir bir şekilde mümkün değildir, çünkü kullanılan terminale çok fazla bağlıdır stdin(bunlar genellikle satır arabelleğe alınır). Ancak bunun için bir kütüphane kullanabilirsiniz:

  1. conio Windows derleyicileri ile kullanılabilir. _getch()Enter tuşunu beklemeden size bir karakter vermek için bu işlevi kullanın . Sık sık bir Windows geliştiricisi değilim, ancak sınıf arkadaşlarımın <conio.h>bunu içerdiğini ve kullandığını gördüm . conio.hWikipedia'da görün . getch()Visual C ++ 'da kullanımdan kaldırıldığı bildirilen listeler .

  2. Linux için mevcut lanetler. Uyumlu lanetler uygulamaları Windows için de kullanılabilir. Ayrıca bir getch()işlevi vardır. ( man getchkendi sayfasını görüntülemeye çalışın ). Bkz . Wikipedia'da Lanetler .

Platformlar arası uyumluluğu hedefliyorsanız lanetler kullanmanızı tavsiye ederim. Yani, eminim ki satır arabelleğe almayı kapatmak için kullanabileceğiniz fonksiyonlar vardır ("pişmiş mod" yerine "ham mod" olarak adlandırılır - bak man stty). Küfürler, yanılmıyorsam senin için taşınabilir bir şekilde hallederdi.


7
Günümüzde, ncursesönerilen bir varyantı olduğunu unutmayın curses.
Monica'nın Davası

4
Eğer bir kütüphaneye ihtiyacınız varsa, bunu nasıl başardılar? kütüphaneyi mutlaka programlamanız gerekiyordu ve bu herkesin programlayabileceği anlamına geliyor, neden sadece kendimize ihtiyacımız olan kütüphane kodunu programlayamıyoruz? o da yapacak saf c ++ değil muhtemelen portably budur yanlış
OverloadedCore

@ kid8 anlamıyorum
Johannes Schaub - litb

1
@ JohannesSchaub-litb Muhtemelen mümkün olmadığını söylediğinizde kütüphane uygulayıcının bu taşınabilir yöntemi saf c / c ++ 'da nasıl yaptığını söylemeye çalışıyor.
Abhinav Gauniyal

@AbhinavGauniyal ah, anlıyorum. Ancak kütüphane uygulayıcının taşınabilir yöntemler (yani makul taşınabilir programlar tarafından kullanılması) yapmadığını söylemedim. Taşınabilir C ++ kullanmadıklarını ima ettim. Açıkça görmedikleri gibi, yorumunun neden cevabımın bu bölümünün yanlış olduğunu söylediğini anlamıyorum.
Johannes Schaub - litb

81

Linux'ta (ve diğer unix benzeri sistemlerde) bu şu şekilde yapılabilir:

#include <unistd.h>
#include <termios.h>

char getch() {
        char buf = 0;
        struct termios old = {0};
        if (tcgetattr(0, &old) < 0)
                perror("tcsetattr()");
        old.c_lflag &= ~ICANON;
        old.c_lflag &= ~ECHO;
        old.c_cc[VMIN] = 1;
        old.c_cc[VTIME] = 0;
        if (tcsetattr(0, TCSANOW, &old) < 0)
                perror("tcsetattr ICANON");
        if (read(0, &buf, 1) < 0)
                perror ("read()");
        old.c_lflag |= ICANON;
        old.c_lflag |= ECHO;
        if (tcsetattr(0, TCSADRAIN, &old) < 0)
                perror ("tcsetattr ~ICANON");
        return (buf);
}

Temel olarak kanonik modu (ve yankıyı bastırmak için eko modunu) kapatmanız gerekir.


Bu kodu uygulamayı denedim, ancak çağrıda bir hata var read. Her iki başlığı da dahil ettim.
Michael Dorst

6
Muhtemelen bu kod yanlış olduğundan, read () unistd.h dosyasında tanımlanan bir POSIX sistem çağrısıdır. stdio.h tesadüfle dahil edilebilir, ancak aslında bu kod için stdio.h'ye ihtiyacınız yoktur; unistd.h ile değiştirin ve iyi olmalı.
Falcon Momot

1
Raspberry Pi'deki ROS terminalinde klavye girişi ararken buraya nasıl geldiğimi bilmiyorum. Bu kod snippet'i benim için çalışıyor.
aknay

2
: Benim NetBeans içinde @FalconMomot diyor (Kali Linux At) 8.1 IDE error: ‘perror’ was not declared in this scopezaman derleme, ancak dahil olduğunda cezası çalışır stdio.hbirlikte unistd.h.
Abhishek Kashyap

3
Bunu engellememek için genişletmenin kolay bir yolu var mı?
Joe Strout

18

Aynı sorunu çözmek için başka bir forumda buldum. Bulduğumdan biraz değiştirdim. Harika çalışıyor. OS X kullanıyorum, bu nedenle Microsoft çalıştırıyorsanız, çiğ ve pişmiş modlara geçmek için doğru system () komutunu bulmanız gerekir.

#include <iostream> 
#include <stdio.h>  
using namespace std;  

int main() { 
  // Output prompt 
  cout << "Press any key to continue..." << endl; 

  // Set terminal to raw mode 
  system("stty raw"); 

  // Wait for single character 
  char input = getchar(); 

  // Echo input:
  cout << "--" << input << "--";

  // Reset terminal to normal "cooked" mode 
  system("stty cooked"); 

  // And we're out of here 
  return 0; 
}

13
Bu işe yararken, değer için, sisteme bombardıman etmek, nadiren bunu yapmanın "en iyi" yoludur. Stty programı C dilinde yazılır, böylece <termios.h> veya <sgtty.h> yazabilir ve harici bir program / fork / whatnot'a bağlı kalmadan stty'nin kullandığı kodu çağırabilirsiniz.
Chris Lutz

1
Kavram malzemelerinin rastgele kanıtı ve ortalığı karıştırmak için buna ihtiyacım vardı. Tam ihtiyacım olan şey. Teşekkür ederim. Unutulmamalıdır: Kesinlikle programın sonunda pişmiş stty koymak istiyorsunuz aksi takdirde kabuk program durduktan sonra temelde benim kabuk lol kırdı stty ham modunda kalacak.
Benjamin

Sanırım unuttun#include <stdlib.h>
NerdOfCode

14

CONIO.H

ihtiyacınız olan fonksiyonlar:

int getch();
Prototype
    int _getch(void); 
Description
    _getch obtains a character  from stdin. Input is unbuffered, and this
    routine  will  return as  soon as  a character is  available  without 
    waiting for a carriage return. The character is not echoed to stdout.
    _getch bypasses the normal buffering done by getchar and getc. ungetc 
    cannot be used with _getch. 
Synonym
    Function: getch 


int kbhit();
Description
    Checks if a keyboard key has been pressed but not yet read. 
Return Value
    Returns a non-zero value if a key was pressed. Otherwise, returns 0.

libconio http://sourceforge.net/projects/libconio

veya

Linux c ++ conio.h uygulaması http://sourceforge.net/projects/linux-conioh


9

Pencerelerde iseniz, herhangi bir giriş olup olmadığını tespit etmek için PeekConsoleInput kullanabilirsiniz ,

HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );

daha sonra girdi karakterini "tüketmek" için ReadConsoleInput komutunu kullanın.

PeekConsoleInput(handle, &buffer, 1, &events);
if(events > 0)
{
    ReadConsoleInput(handle, &buffer, 1, &events);  
    return buffer.Event.KeyEvent.wVirtualKeyCode;
}
else return 0

dürüst olmak gerekirse bu benim bazı eski kod, bu yüzden onunla biraz keman zorunda.

Harika şey, herhangi bir şey sormadan girişi okumasıdır, bu yüzden karakterler hiç görüntülenmez.


9
#include <conio.h>

if (kbhit() != 0) {
    cout << getch() << endl;
}

Bu kbhit(), klavyenin basılı olup olmadığını kontrol etmek için ve basılan getch()karakteri almak için kullanılır.


5
conio.h ? "conio.h eski MS-DOS derleyicilerinde metin kullanıcı arabirimleri oluşturmak için kullanılan bir C başlık dosyasıdır." Biraz modası geçmiş gibi görünüyor.
kay - SE is evil


5

C ve C ++, G / Ç'nin çok soyut bir görünümünü alır ve istediğinizi yapmanın standart bir yolu yoktur. Karakterleri standart girdi akışından almanın standart yolları vardır, alınacak herhangi bir şey varsa ve bu iki dil tarafından başka bir şey tanımlanmamıştır. Bu nedenle, herhangi bir yanıtın platforma özel olması gerekir, belki de sadece işletim sistemine değil, yazılım çerçevesine de bağlı olacaktır.

Burada bazı makul tahminler var, ancak hedef ortamınızın ne olduğunu bilmeden sorunuza cevap vermenin bir yolu yok.


5

Ben bir karakter olup olmadığını görmek için kbhit () kullanın ve sonra veri okumak için getchar () kullanın. Pencerelerde "conio.h" kullanabilirsiniz. Linux'ta kendi kbhit'inizi () uygulamanız gerekecektir.

Aşağıdaki koda bakın:

// kbhit
#include <stdio.h>
#include <sys/ioctl.h> // For FIONREAD
#include <termios.h>
#include <stdbool.h>

int kbhit(void) {
    static bool initflag = false;
    static const int STDIN = 0;

    if (!initflag) {
        // Use termios to turn off line buffering
        struct termios term;
        tcgetattr(STDIN, &term);
        term.c_lflag &= ~ICANON;
        tcsetattr(STDIN, TCSANOW, &term);
        setbuf(stdin, NULL);
        initflag = true;
    }

    int nbbytes;
    ioctl(STDIN, FIONREAD, &nbbytes);  // 0 is STDIN
    return nbbytes;
}

// main
#include <unistd.h>

int main(int argc, char** argv) {
    char c;
    //setbuf(stdout, NULL); // Optional: No buffering.
    //setbuf(stdin, NULL);  // Optional: No buffering.
    printf("Press key");
    while (!kbhit()) {
        printf(".");
        fflush(stdout);
        sleep(1);
    }
    c = getchar();
    printf("\nChar received:%c\n", c);
    printf("Done.\n");

    return 0;
}

4

ncursesPortala en yakın şey , terminali "kırılma moduna" sokmak için kütüphaneyi kullanmaktır . API devasa; en çok isteyeceğiniz rutinler

  • initscr ve endwin
  • cbreak ve nocbreak
  • getch

İyi şanslar!


3

Aşağıda Uzman C Programlamasından çıkarılan bir çözüm yer almaktadır : SVr4 üzerinde çalışması gereken Derin Sırlar . Stty ve ioctl kullanır .

#include <sys/filio.h>
int kbhit()
{
 int i;
 ioctl(0, FIONREAD, &i);
 return i; /* return a count of chars available to read */
}
main()
{
 int i = 0;
 intc='';
 system("stty raw -echo");
 printf("enter 'q' to quit \n");
 for (;c!='q';i++) {
    if (kbhit()) {
        c=getchar();
       printf("\n got %c, on iteration %d",c, i);
    }
}
 system("stty cooked echo");
}

3

Her zaman dönüş tuşuna basmadan bir döngüyü girişimi okumak istedim. bu benim için çalıştı.

#include<stdio.h>
 main()
 {
   char ch;
    system("stty raw");//seting the terminal in raw mode
    while(1)
     {
     ch=getchar();
      if(ch=='~'){          //terminate or come out of raw mode on "~" pressed
      system("stty cooked");
     //while(1);//you may still run the code 
     exit(0); //or terminate
     }
       printf("you pressed %c\n ",ch);  //write rest code here
      }

    }

1
RAW MODE'da olduğunuzda işlemi öldürmek zordur. bu yüzden "~" tuşuna bastığım gibi işlemi öldürmek için bir noktaya dikkat edin. ................ diğer akıllıca işlemi KILL kullanarak diğer terminalden öldürebilirsiniz.
Setu Gupta

3

ncurses bunu yapmanın güzel bir yoludur! Ayrıca bu benim ilk yazı (ki hatırlıyorum), bu yüzden herhangi bir yorum bekliyoruz. Yararlı olanları takdir edeceğim, ama hepsi açıktır!

derlemek için: g ++ -std = c ++ 11 -pread -lncurses .cpp -o

#include <iostream>
#include <ncurses.h>
#include <future>

char get_keyboard_input();

int main(int argc, char *argv[])
{
    initscr();
    raw();
    noecho();
    keypad(stdscr,true);

    auto f = std::async(std::launch::async, get_keyboard_input);
    while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
    {
        // do some work
    }

    endwin();
    std::cout << "returned: " << f.get() << std::endl;
    return 0;
}

char get_keyboard_input()
{
    char input = '0';
    while(input != 'q')
    {
        input = getch();
    }
    return input;
}

2

benim için pencerelerde çalışır:

#include <conio.h>
char c = _getch();

1

SDL (Basit DirectMedia Kütüphanesi) kullanarak portatif olarak yapabilirsiniz, ancak davranışını beğenmeyeceğinizden şüpheleniyorum. Denediğimde, SDL'nin yeni bir video penceresi oluşturması gerekiyordu (programım için buna ihtiyacım olmasa bile) ve bu pencerenin neredeyse tüm klavye ve fare girişini "kullanmam" gerekiyordu (ki bu benim kullanımım için iyiydi, ancak diğer durumlarda can sıkıcı veya işsiz olabilir). Aşırı taşınabilirlik ve tam taşınabilirlik şart olmadığı sürece buna değmez - aksi takdirde önerilen diğer çözümlerden birini deneyin.

Bu arada, bu tuşa basarsanız, tuşa basma ve bırakma olaylarını ayrı ayrı verecektir.


1

İşte sisteme kabuk oluşturmayan bir sürüm (macOS 10.14'te yazılmış ve test edilmiştir)

#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>

char* getStr( char* buffer , int maxRead ) {
  int  numRead  = 0;
  char ch;

  struct termios old = {0};
  struct termios new = {0};
  if( tcgetattr( 0 , &old ) < 0 )        perror( "tcgetattr() old settings" );
  if( tcgetattr( 0 , &new ) < 0 )        perror( "tcgetaart() new settings" );
  cfmakeraw( &new );
  if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );

  for( int i = 0 ; i < maxRead ; i++)  {
    ch = getchar();
    switch( ch )  {
      case EOF: 
      case '\n':
      case '\r':
        goto exit_getStr;
        break;

      default:
        printf( "%1c" , ch );
        buffer[ numRead++ ] = ch;
        if( numRead >= maxRead )  {
          goto exit_getStr;
        }
        break;
    }
  }

exit_getStr:
  if( tcsetattr( 0 , TCSADRAIN , &old) < 0)   perror ("tcsetattr reset to old" );
  printf( "\n" );   
  return buffer;
}


int main( void ) 
{
  const int maxChars = 20;
  char      stringBuffer[ maxChars+1 ];
  memset(   stringBuffer , 0 , maxChars+1 ); // initialize to 0

  printf( "enter a string: ");
  getStr( stringBuffer , maxChars );
  printf( "you entered: [%s]\n" , stringBuffer );
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.