"Devam etmek için herhangi bir tuşa basın" simülasyonu nasıl yapılır?


87

Kullanıcı klavyeden herhangi bir karakter girdiğinde ve bir sonraki kod satırına geçmesi gereken bir C ++ programı yazmaya çalışıyorum.

İşte kodum:

char c;

cin>>c;

cout<<"Something"<<endl;

ancak bu çalışmıyor, çünkü sadece bir karakter girip ENTER tuşuna bastığımda bir sonraki satıra geçiyor.

VEYA

Bunu kullanırsam

cin.get() or cin.get(c)

Enter tuşuna bastığımda bir sonraki talimat satırına geçiyor.

Ama klavyede basılan herhangi bir tuşta bir sonraki satıra geçmesini istedim, bu nasıl yapılabilir?


Bildiğim kadarıyla sorun şu ki, kabuğunuz ENTER veya EOF tuşuna basmanızı bekliyor ve ardından programınızın arabellekte ne varsa veya bunun gibi bir şeyle ilgilenmesine izin veriyor. Belki biraz daha fazla bilgiye sahip biri gerçek bir açıklama sağlayabilir. Ama ilk göründüğü kadar kolay olmadığını düşünüyorum.
Lucas

7
Bunu halletmenin en kolay yolu ve taşınabilir tek yol, isteminizi "Devam etmek için herhangi bir tuşa basın" dan "Devam etmek için Enter tuşuna basın" olarak değiştirmektir.
rob mayoff

@Lucas - xcode ile mac kullanıyorum.
itsaboutcode

@Lucas: Kabuk değil, programın kendisi.
Keith Thompson

@KeithThompson: Bu iki yıldan daha uzun bir süre önceydi, ama sanırım girdi kuyruğunun kullanıcı süreci tarafından değil, Kernel içinde ele alındığına işaret etmeye çalışıyordum.
Lucas

Yanıtlar:


65

Windows'ta:

system("pause");

ve Mac ve Linux'ta:

system("read");

"Devam etmek için herhangi bir tuşa basın ..." çıktısı verecektir ve belli ki, herhangi bir tuşa basılmasını bekleyin. Umarım kastettiğin budur


5
systemharici programı çağırır. Herhangi bir tuşu beklemek için harici programı aramanıza gerek yok! Önünde otururken bilgisayarınızı açması için başka birini aramak gibi - bu yavaş bir çözümdür.
GingerPlusPlus

8
Doğru, ama sadece bir satır uzunluğunda! Bazen bilgisayarı birkaç döngüden kurtarmak için gösterilen çabaya değmez ...
Elliot Cameron

14
Yavaşlığı unutun, PATH'de bulduğu "duraklatma" adlı ilk programı çağırır ve ona çağıran programın ayrıcalık seviyesini verir. Sadece kendi kodunuzu test eden bir öğrenci olsanız bile, bu yine de büyük bir güvenlik riski.
Anne Quinn

6
@XDfaceme - duraklama, system () tarafından pencerelerin çalıştırılmasını istediği gerçek bir programdır. Ancak, 'duraklatma' adında başka bir program varsa ve Windows bu programı önce bulursa, onun yerine çalıştırır. Ben önemi biraz abartılı ettik olabilir varsayalım, ama bir programa devam ettirmek / adil kullanım cin.get () ve duraklama için isabet dönüş hala kolay
Anne Quinn

2
Mutlakların hayranı değilim. Sistemin ("duraklatmanın") bir güvenlik riski olduğu ve kendi kodunuzu test ederken bile kötü olduğu konusunda genel bir açıklama yapmak, burada işleri orantısız bir şekilde uçuruyor. cin.get () doğru çözüm olabilir, ancak sorulan soruyu çözmez ... cin.get () yalnızca "enter" veya "return" tuşuna basıldıktan sonra yanıt verir. Soru özellikle herhangi bir tuşa basılmasına yanıt vermekti. system ("pause") tam olarak bunu yapar. Her şeyden önce, bunu yararlı olarak gördüğüm tek yer, Visual Studio'dan hata ayıklarken bir geliştirici sınıfındadır, bu yüzden sistemin ("duraklatma") çalıştığını düşünüyorum
Gregor

53

Windows kullanıyorsanız kbhit(), Microsoft çalışma zamanı kitaplığının bir parçası olanı kullanabilirsiniz . Linux'taysanız, şu şekilde uygulayabilirsiniz kbhit( kaynak ):

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

int kbhit(void)
{
  struct termios oldt, newt;
  int ch;
  int oldf;

  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

  ch = getchar();

  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  fcntl(STDIN_FILENO, F_SETFL, oldf);

  if(ch != EOF)
  {
    ungetc(ch, stdin);
    return 1;
  }

  return 0;
}

Güncelleme: Yukarıdaki işlev OS X'te çalışır (en azından OS X 10.5.8 - Leopard'da, bu yüzden OS X'in daha yeni sürümlerinde çalışmasını beklerim). Bu özetkbhit.c hem Linux hem de OS X olarak kaydedilebilir ve derlenebilir.

gcc -o kbhit kbhit.c

İle çalıştırıldığında

./kbhit

Sizden bir tuşa basmanızı ister ve bir tuşa bastığınızda çıkar (Enter veya yazdırılabilir tuşlarla sınırlı değildir).

@Johnsyweb - lütfen "ayrıntılı kanonik yanıt" ve "tüm endişeler" ile ne demek istediğinizi açıklayın . Ayrıca, yeniden "çapraz platform": Bu uygulama kbhit()ile Linux / Unix / OS X / Windows üzerindeki bir C ++ programında aynı işlevselliğe sahip olabilirsiniz - başka hangi platformlardan bahsediyor olabilirsiniz?

@Johnsyweb için daha fazla güncelleme: C ++ uygulamaları hava geçirmez şekilde kapatılmış bir C ++ ortamında yaşamaz. C ++ 'ın başarısının büyük bir nedeni C ile birlikte çalışabilirliktir. Tüm ana platformlar C arayüzleri ile gerçekleştirilir (dahili uygulama C ++ kullanıyor olsa bile), bu nedenle "miras" konusundaki konuşmanız yersiz görünüyor. Artı, tek bir işlevden bahsederken, bunun için neden C ++ 'ya ihtiyacınız var ("sınıflarla C")? Daha önce de belirttiğim gibi, C ++ ile yazabilir ve bu işlevselliğe kolayca erişebilirsiniz ve uygulamanızın kullanıcılarının bunu nasıl uyguladığınızı umursama olasılığı düşüktür .


Konsolu kullanmanın standart bir yolu olmaması utanç verici, her zaman biraz işletim sistemi büyüsü kullanmanız gerekiyor
Vargas

1
@Johnsyweb: Teşekkür ederim , ancak örneğiniz beni C ++ konusunda üzen özellikler gösteriyor. Sizin kbhit()işlevi beyan terminal_settingshiçbir şey gibi görünen bir çizgide değişkeni, ama yine de her şeyi yapıyor. Bazıları bunun düzgün olduğunu düşünebilir, ancak ben onu okunamazlık açısından belirsiz buluyorum.
Vinay Sajip

1
@VinaySajip: My uygulaması kullanır de ray C ++ en önemli deyimler biridir. Bu örnekte kesinlikle gerekli olmasa da, bir durumu kaydetmek ve onu geri yüklemek istediğinizde içine girmeyi iyi bir alışkanlık buldum.
Johnsyweb

4
@Johnsyweb: RAII'nin ve getirdiği faydaların farkındayım, ben de eski bir C ++ eliyim. Demek istediğim şu: deklarasyon ile perde arkasında yaptıkları arasında net bir bağlantı yok. Düz eski C yerine parlak C ++ deyimleri kullanıyor olsa da, benim görüşüme göre, aydınlatmak için çok az şey ve neler olup bittiğini gizlemek için çok şey yapıyor. Bu, kişisel olarak size herhangi bir şekilde yönelik değildir - sadece, C ++ 'nın, anlaşılması gereksiz yere zor olan kod oluşturmak için nasıl kullanılabileceği hakkında bir fikirdir. Şimdi sabun kutumdan çıkacağım, 'dedi nuff.
Vinay Sajip

2
Bu güzel ve hepsi, ama yine de stdin'de önceki çöpleri yakalıyor :-)
Tiago

9

Tamamen taşınabilir bir çözüm yoktur.

Comp.lang.c SSS'nin 19.1 Sorusu , Windows, Unix benzeri sistemler ve hatta MS-DOS ve VMS çözümleri ile bunu biraz daha derinlemesine kapsar.

Hızlı ve eksik bir özet:

  • cursesKitaplığı kullanabilirsiniz ; çağrı ve cbreak()ardından getch()(Windows'a özgü getch()işlevle karıştırılmamalıdır ). O Not cursesbu overkill olması muhtemeldir bu yüzden genel olarak, terminalin kontrolünü ele alır.
  • ioctl()Terminal ayarlarını değiştirmek için kullanabilirsiniz .
  • POSIX uyumlu sistemlerde tcgetattr()ve tcsetattr()daha iyi bir çözüm olabilir.
  • Unix'te, komutu system()çağırmak için kullanabilirsiniz stty.
  • MS-DOS'ta getch()veya kullanabilirsiniz getche().
  • VMS'de (şimdi OpenVMS olarak adlandırılır), Ekran Yönetimi ( SMG$) rutinleri hile yapabilir.

Tüm bu C çözümleri, C ++ 'da eşit derecede iyi çalışmalıdır; C ++ 'ya özgü herhangi bir çözüm bilmiyorum.


Tüm bu çok platformlu çözümler, platformunuza bağlı olarak doğru şeyi yapmak için uygulanan bir işlevi yazarken, bu platformun ne olduğunu belirlemek için çok sayıda ön işlemciye sahip olabilir.
CashCow

6

Bu işlevselliği elde etmek için hem Windows hem de Linux'ta (ve bildiğim kadarıyla MacOS'ta) uygulanan ncurses kitaplığını kullanabilirsiniz .


Bu, herhangi bir harici kitaplık vb. Kullanamadığım bir tür ödevdir, bu yüzden sadece "bölüm bağlamı" lox'inde kalmak zorundayım.
itsaboutcode

2
O zaman tek seçenek, desteklemeyi planladığınız her işletim sistemi için gerekli işlevselliği uygulamaktır.
Kirill V. Lyadvinsky

1
ncurses aşağı yukarı bir VT200 istemcisidir. TTY'den okumak için biraz fazla.
greyfade

5

Pencerelerde, bu kısa program hedefi gerçekleştirir: getchkonsolu bir tuşa basılıncaya kadar duraklatır ( https://www.geeksforgeeks.org/difference-getchar-getch-getc-getche/ )

#include<iostream.h>
#include<conio.h>

using namespace std;

void  check()
{
    char chk; int j;
    cout<<"\n\nPress any key to continue...";
    chk=getch();
    j=chk;
    for(int i=1;i<=256;i++)
      if(i==j) break;
    clrscr();
}

void main()
{
    clrscr();
    check();
    cout<<"\n\nIt works!";
    getch();
}

Getch'in standart kitaplığın bir parçası olmadığı unutulmamalıdır.


1
Bu çok basit program. Anlaşılması çok kolay
BoldX

1
Lütfen nasıl getch()farklı olduğunu açıklayın cin.getveya cin>>belki belgelere bir bağlantı
user2622016

1
conio yalnızca Windows'ta
Andrew Wolfe

4

Neyi başarmaya çalıştığınıza baktım, çünkü aynı şeyi yapmak istediğimi hatırlıyorum. Vinay'den esinlenerek benim için işe yarayan bir şey yazdım ve anlıyorum. Ama ben uzman değilim, lütfen dikkatli olun.

Vinay'in Mac OS X kullandığınızı nasıl bildiğini bilmiyorum. Ancak çoğu unix benzeri işletim sistemi ile bu şekilde çalışması gerekir. Kaynak olarak gerçekten yararlıdır opengroup.org

İşlevi kullanmadan önce tamponu yıkadığınızdan emin olun.

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


void pressKey()
{
    //the struct termios stores all kinds of flags which can manipulate the I/O Interface
    //I have an old one to save the old settings and a new 
    static struct termios oldt, newt;
    printf("Press key to continue....\n");

    //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;

    //two of the c_lflag will be turned off
    //ECHO which is responsible for displaying the input of the user in the terminal
    //ICANON is the essential one! Normally this 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 | ECHO );      

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

    //now the char wil be requested
    getchar();

    //the old settings will be written back to STDIN
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);

}


int main(void)
{
  pressKey();
  printf("END\n");
  return 0;
}

O_NONBLOCK da önemli bir bayrak gibi görünüyor, ancak benim için hiçbir şeyi değiştirmedi.

Biraz daha derin bilgiye sahip insanların bu konu hakkında yorum yapmasını ve tavsiyelerde bulunmasını takdir ediyorum.


O_NONBLOCK ayarlanmalıdır çünkü onsuz getchar () (read () çağrıları) girdi beklemeyi engelleyecektir. Kbhit () çözümü, beklemeden bir tuşa basılıp basılmadığını söyler; onu bir bekleme döngüsünde veya başka bir amaç için kullanabilirsiniz, bu nedenle daha genel bir çözümdür. O_NONBLOCK içermeyen bir çözüm, bir tuşa basılıncaya kadar bekleyecektir - Bu soru için Tamam, ancak genel olarak daha az yararlıdır.
Vinay Sajip

4

Microsoft'a özgü _getch işlevini kullanabilirsiniz :

#include <iostream>
#include <conio.h>
// ...
// ...
// ...
cout << "Press any key to continue..." << endl;
_getch();
cout << "Something" << endl;

3

Bu bir Windows Platformunda çalışır: Mikroişlemci kayıtlarını doğrudan kullanır ve tuşa basmayı veya fare düğmesini kontrol etmek için kullanılabilir.

    #include<stdio.h>
    #include<conio.h>
    #include<dos.h>
    void main()
    {
     clrscr();
     union REGS in,out;
     in.h.ah=0x00;
     printf("Press any key : ");

     int86(0x16,&in,&out);
     printf("Ascii : %d\n",out.h.al);
     char ch = out.h.al;
     printf("Charcter Pressed : %c",&ch);
     printf("Scan code : %d",out.h.ah);
     getch();
    }

3

Visual Studio 2012 veya daha eski kullanıyorsanız getch()işlevi kullanın, Visual Studio 2013 veya daha yenisini kullanıyorsanız _getch(). Kullanmanız gerekecek #include <conio.h>. Misal:

#include <iostream>
#include <conio.h>

int main()
{
   std::cout << "Press any key to continue. . .\n";
   _getch(); //Or getch()
}

1

Getchar rutinini kullanabilirsiniz .

Yukarıdaki bağlantıdan:

/* getchar example : typewriter */
#include <stdio.h>

int main ()
{
  char c;
  puts ("Enter text. Include a dot ('.') in a sentence to exit:");
  do {
    c=getchar();
    putchar (c);
  } while (c != '.');
  return 0;
}

3
Eğer onu doğru anlarsam, OP'nin istediği şeyin bu olduğunu sanmıyorum. Getchar () 'ın okuyabilmesi için önce tamponu doldurmak için ENTER tuşuna basmanız gerekir.
Lucas

0

Ayrıca conio.h dosyasındaki getch () işlevini de kullanabilirsiniz. Aynen böyle:

...includes, defines etc
void main()
{
//operator
getch(); //now this function is waiting for any key press. When you have pressed its     just     //finish and next line of code will be called
}

Öyleyse, UNIX'te conio.h olmadığı için, bu kodla getch () simülasyonunu yapabiliriz (ancak bu kod zaten Vinary tarafından yazılmıştır, benim hatam):

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

int mygetch( ) {
  struct termios oldt,
             newt;
  int            ch;
  tcgetattr( STDIN_FILENO, &oldt );
  newt = oldt;
  newt.c_lflag &= ~( ICANON | ECHO );
  tcsetattr( STDIN_FILENO, TCSANOW, &newt );
  ch = getchar();
  tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
  return ch;
}

0

Diğer bir seçenek, işlev işaretçileri olan iş parçacıkları kullanmaktır :

#include <iostream>
#include <thread> // this_thread::sleep_for() and thread objects
#include <chrono> // chrono::milliseconds()

bool stopWaitingFlag = false;

void delayms(int millisecondsToSleep) 
{
  std::this_thread::sleep_for(std::chrono::milliseconds(millisecondsToSleep));
}

void WaitForKey() 
{
  while (stopWaitingFlag == false)
  {
    std::cout<<"Display from the thread function"<<std::endl;
    delayms(1000);
  }
}

 

int main()
{
  std::thread threadObj(&WaitForKey);

  char userInput = '\0';
  while (userInput != 'y')
  {
    std::cout << "\e[1;31mWaiting for a key, Enter 'y' for yes\e[0m" << std::endl;
    std::cin >> userInput;
    if (userInput == 'y') {
      stopWaitingFlag = true;
    }
  }
  if (threadObj.joinable())
    threadObj.join();

  std::cout<<"Exit of Main function"<<std::endl;
  return 0;
}

-1

Şimdi MSDN'de kbhit () işlevine bakarsanız, işlevin kullanımdan kaldırıldığını belirtir. Bunun yerine _kbhit () kullanın.

#include <conio.h>
int main()
{
    _kbhit();
    return 0;
}

-1
#include <iostream>
using namespace std;

int main () {
    bool boolean;
    boolean = true;

    if (boolean == true) {

        cout << "press any key to continue";
        cin >> boolean;

    }
    return 0;
}

-4

Sadece system("pause");komutu kullanın.

Diğer tüm cevaplar sorunu karmaşıklaştırıyor.

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.