2012-11-17 2 views
4

Je voudrais écrire un petit programme C qui exécute une boucle infinie jusqu'à ce que l'utilisateur appuie sur une touche du clavier (ie: il y a un caractère dans le tampon stdin). Je rencontre des problèmes en cassant la boucle sur l'entrée de l'utilisateur. J'ai essayé d'utiliser fgetc mais cela ne se comporte pas comme prévu. Le code ci-dessous attend pour l'entrée de l'utilisateur plutôt que d'exécuter jusqu'à l'entrée utilisateur.Comment faire pour qu'un programme C arrête une boucle à partir de n'importe quelle entrée utilisateur?

Exemple de code C:

while((c=fgetc(stdin) == EOF) { 
    /* Does stuff for infinite loop here */ 
    printf("Example work in the loop\n"); 
} 
printf("Out of the loop!\n"); 

Comment puis-je écrire une boucle qui exécute jusqu'à intervention de l'utilisateur? Appuyer sur n'importe quelle touche ou une touche spécifique pourrait être le déclencheur de l'intervention.

Note 1: Je vous écris ceci pour une console Unix en cas de plate-forme de solutions spécifiques

Note 2: Ne suggérez pas Ctrl + C/X/Z comme déclencheur d'intervention de l'utilisateur

+0

connexes: http://stackoverflow.com/questions/149860/how-to-prevent-fgets-blocks-when -file-stream-has-no-new-data – tjameson

+0

duplication possible de [C entrée clavier non-bloquante] (http://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input) – fvu

+0

Si votre programme a un tty pour stdin, un utilisateur en tapant une clé ne fournira aucune entrée au programme. Le tty n'enverra rien à votre programme tant que l'utilisateur n'aura pas tapé 'enter' ou envoyé un signal via^C ou ^] ou^Z. Clarifiez votre question. Voulez-vous répondre à une entrée ou souhaitez-vous répondre à un événement de clavier? Les deux sont différents. –

Répondre

4

Cela semble fonctionner pour moi:

#include <fcntl.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/uio.h> 
#include <unistd.h> 

static void set_non_blocking(int fd) 
{ 
    int flags = fcntl(fd, F_GETFL, 0); 
    flags |= O_NONBLOCK; 
    flags = fcntl(fd, F_SETFL, flags); 
} 


int main(int argc, char ** argv) 
{ 
    int fd = fileno(stdin); 
    char buf[10]; 

    set_non_blocking(fd); 

    while (read(fd, buf, sizeof buf) < 0) { 
     perror("read"); 
     sleep(1); 
    } 
    return 0; 
} 

ou vous pouvez utiliser select:

int main(int argc, char ** argv) 
{ 
    int fd = fileno(stdin); 
    struct timeval tv = {0,0}; 
    fd_set fdset; 
    int s; 

    do { 
     sleep(1); 
     FD_ZERO(&fdset); 
     FD_SET(fd, &fdset); 

    } while ((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 0); 

    if (s < 0) { 
     perror("select"); 
    } 
    return 0; 
} 

Sondage fonctionne aussi :-)

int main(int argc, char ** argv) 
{ 
    struct pollfd pfd; 
    int s; 

    pfd.fd = fileno(stdin); 
    pfd.events = POLLRDNORM; 

    while ((s = poll(&pfd, 1, 0)) == 0) { 
     perror("polling"); 
     sleep(1); 
    } 
    if (s < 0) { 
     perror("poll"); 
    } 
    return 0; 
} 

Une dernière façon est de mettre le terminal en mode 'brut'. Notez que ceci perturbe la sortie vers le terminal (au moins sur le mien sous OS-X) en ce que \ r devient nécessaire après \ n. Notez également qu'il doit être annulé à la fin (l'appel tcsetattr de fin). Ceci est le seul qui n'a pas besoin d'un \ n (ie fera pression sur une touche)

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


static void set_non_blocking(int fd) 
{ 
    int flags = fcntl(fd, F_GETFL, 0) | O_NONBLOCK; 

    if (fcntl(fd, F_SETFL, flags) < 0) { 
     perror("fcntl"); 
     exit(EXIT_FAILURE); 
    } 
} 


int main(int argc, char ** argv) 
{ 
    struct termios params; 
    struct termios params_orig; 
    char buf[10]; 
    int fd = fileno(stdin); 

    if (tcgetattr(fd, &params) < 0) { 
     perror("tcgetattr"); 
     exit(EXIT_FAILURE); 
    } 
    params_orig = params; 

    cfmakeraw(&params); 

    if (tcsetattr(fd, TCSANOW, &params) < 0) { 
     perror("tcsetattr"); 
     exit(EXIT_FAILURE); 
    } 
    set_non_blocking(fd); 

    while (read(fd, buf, sizeof buf) < 0) { 
     perror("\rread"); 
     sleep(1); 
    } 

    (void) tcsetattr(fd, TCSANOW, &params_orig); 
    return 0; 
} 
+0

Cela semble fonctionner pour moi aussi! (seulement lorsque la touche Entrée est atteinte) –

+0

J'aime 'select()' parce que ce n'est pas spécifique à * nix –

Questions connexes