Estoy tratando de escribir un programa en C (en Linux) que se repite hasta que el usuario presiona una tecla, pero que no requieren una tecla para continuar cada bucle.

Hay una manera simple de hacer esto? Me imagino que podría hacer con select() pero que parece como un montón de trabajo.

Alternativamente, hay una manera de atrapar a un ctrlc pulsación de teclas para hacer la limpieza antes de que el programa se cierra en lugar de no-bloqueo de io?

InformationsquelleAutor Zxaos | 2009-01-15

10 Comentarios

  1. 61

    Como ya se ha dicho, puede utilizar sigaction para capturar ctrl-c, o select para atrapar cualquier entrada estándar.

    Nota sin embargo de que con este último método también es necesario establecer el TTY, así que es de carácter-en-un-tiempo en lugar de la línea-en-un-modo de tiempo. El último es el valor predeterminado si se escribe en una línea de texto no se envía a la ejecución del programa de stdin hasta que pulse enter.

    Que había necesidad de utilizar el tcsetattr() función para apagar ICANON modo, y probablemente también desactiva el ECHO demasiado. De la memoria, usted también tiene que fijar el terminal de nuevo en ICANON modo cuando se sale del programa!

    Sólo para la integridad, aquí está el código que acabo de knocked up (nota: sin comprobación de error!) lo que configura un Unix TTY y emula las DOS <conio.h> funciones kbhit() y getch():

    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/select.h>
    #include <termios.h>
    
    struct termios orig_termios;
    
    void reset_terminal_mode()
    {
        tcsetattr(0, TCSANOW, &orig_termios);
    }
    
    void set_conio_terminal_mode()
    {
        struct termios new_termios;
    
        /* take two copies - one for now, one for later */
        tcgetattr(0, &orig_termios);
        memcpy(&new_termios, &orig_termios, sizeof(new_termios));
    
        /* register cleanup handler, and set the new terminal mode */
        atexit(reset_terminal_mode);
        cfmakeraw(&new_termios);
        tcsetattr(0, TCSANOW, &new_termios);
    }
    
    int kbhit()
    {
        struct timeval tv = { 0L, 0L };
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(0, &fds);
        return select(1, &fds, NULL, NULL, &tv);
    }
    
    int getch()
    {
        int r;
        unsigned char c;
        if ((r = read(0, &c, sizeof(c))) < 0) {
            return r;
        } else {
            return c;
        }
    }
    
    int main(int argc, char *argv[])
    {
        set_conio_terminal_mode();
    
        while (!kbhit()) {
            /* do some work */
        }
        (void)getch(); /* consume the character */
    }
    • Es getch() debe devolver lo que ha pulsado la tecla, o es simplemente supone que consume el personaje?
    • se supone que debe de devolver el carácter. Es el (void) bits que consigue que el carácter y, a continuación, se tira a la basura.
    • oh ya veo, tal vez lo estoy haciendo mal, pero cuando puedo usar getch() en una printf(«%c»); muestra caracteres extraños en la pantalla.
    • Ligero modificar, usted necesita #include <unistd.h> para leer (a) a trabajar
    • Hay una forma simple de leer toda la línea (por ejemplo, con ‘getline’) en lugar de un solo personaje ?
    • Comprueba en OSX mavericks funciona como un encanto !!! Muchas gracias señor!

  2. 15

    select() es un poco demasiado bajo nivel de comodidad. Le sugiero que use el ncurses de la biblioteca para poner el terminal en quebrantará el modo y el modo de retardo, a continuación, llamar getch(), que volverá ERR si no hay ningún personaje está listo:

    WINDOW *w = initscr();
    cbreak();
    nodelay(w, TRUE);

    En ese momento usted puede llamar a getch sin bloqueo.

    • Pensé sobre el uso de maldiciones, pero el problema con esto es que la initscr() la llamada borra la pantalla, y también tal vez se interpone en el camino de la utilización normal de stdio para la salida de la pantalla.
    • Sí, maldiciones es bastante de todo o nada.
  3. 11

    Que en los sistemas UNIX, puede utilizar sigaction llamada a registrar un manejador de señal para SIGINT de la señal que representa el Control+C secuencia de teclas. El manejador de señal puede establecer un indicador que será verificado en el bucle de lo que es para romper de forma adecuada.

    • Creo que probablemente va a terminar haciendo esto por gusto, pero voy a aceptar la otra respuesta, ya que está más cerca de lo que el título de la pregunta.
  4. 6

    Usted probablemente querrá kbhit();

    //Example will loop until a key is pressed
    #include <conio.h>
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        while(1)
        {
            if(kbhit())
            {
                break;
            }
        }
    }

    esto puede no funcionar en todos los ambientes. Una manera portátil sería crear un subproceso de supervisión y establecer algunas bandera en getch();

    • definitivamente no es portátil. kbhit() es una de DOS/Windows consola IO de la función.
    • Es todavía una respuesta válida muchos usos. El OP no especificar la portabilidad o cualquier plataforma en particular.
    • no. sin embargo el uso de términos tales como «no bloqueo», sugiere Unix.
    • Alnitak: yo no era consciente de ello implicaba una plataforma – ¿cuál es el equivalente de Windows plazo?
    • usted puede hacer sin bloqueo de e / s en windows, pero el concepto (junto con el uso de select) es más familiar para los programadores de Unix (en mi humilde opinión)
    • ¿por qué «no bloqueo», como un concepto de programación, ser más familiar en un entorno Unix?
    • Tal vez ahora (seis años después) que no es el caso. Siempre estuvo ahí en POSIX, aunque.
    • Me refiero a que yo estaba familiarizado con el término «no-bloqueo de la llamada» mucho antes de tocar cualquier *nix.. así como Zxaos nunca me doy cuenta de que implicaría una plataforma.

  5. 3

    Otra forma de no-bloqueo de la entrada del teclado para abrir el archivo de dispositivo y leer!

    Usted tiene que saber el archivo de dispositivo que usted está buscando, uno de /dev/input/evento*. Puede ejecutar cat /proc/bus/input/dispositivos para encontrar el dispositivo que desee.

    Este código funciona para mí (ejecutar como administrador).

      #include <stdlib.h>
      #include <stdio.h>
      #include <unistd.h>
      #include <fcntl.h>
      #include <errno.h>
      #include <linux/input.h>
    
      int main(int argc, char** argv)
      {
          int fd, bytes;
          struct input_event data;
    
          const char *pDevice = "/dev/input/event2";
    
          //Open Keyboard
          fd = open(pDevice, O_RDONLY | O_NONBLOCK);
          if(fd == -1)
          {
              printf("ERROR Opening %s\n", pDevice);
              return -1;
          }
    
          while(1)
          {
              //Read Keyboard Data
              bytes = read(fd, &data, sizeof(data));
              if(bytes > 0)
              {
                  printf("Keypress value=%x, type=%x, code=%x\n", data.value, data.type, data.code);
              }
              else
              {
                  //Nothing read
                  sleep(1);
              }
          }
    
          return 0;
       }
  6. 3

    Las maldiciones de la biblioteca se puede utilizar para este propósito. Por supuesto, select() y manejadores de señal puede ser usado también en cierta medida.

    • maldiciones es tanto el nombre de la biblioteca y lo que va a murmurar cuando se utiliza 😉 sin Embargo, desde que yo iba a hablar de ti +1 para ti.
  7. 2

    Si usted es feliz sólo la captura de Control-C, es un trato hecho. Si usted realmente quiere sin bloqueo de e/S, pero usted no quiere que las maldiciones de la biblioteca, otra alternativa es pasar lock, stock, y el barril para la AT&T sfio biblioteca. Es bonito estampado de la biblioteca en C stdio, pero más flexible, hilo de seguridad, y funciona mejor. (sfio representa seguro, rápido I/O.)

  8. 1

    No hay manera portátil para hacer esto, pero seleccionar() puede ser una buena manera. Ver http://c-faq.com/osdep/readavail.html para obtener más posibles soluciones.

    • esta respuesta es incompleta. sin terminal de manipulación con tcsetattr() [véase mi respuesta] no se consigue nada en fd 0 hasta que se pulse enter.
  9. 0

    Aquí una función para hacer esto para usted. Usted necesita termios.h que viene con POSIX sistemas.

    #include <termios.h>
    void stdin_set(int cmd)
    {
        struct termios t;
        tcgetattr(1,&t);
        switch (cmd) {
        case 1:
                t.c_lflag &= ~ICANON;
                break;
        default:
                t.c_lflag |= ICANON;
                break;
        }
        tcsetattr(1,0,&t);
    }

    Romper esta abajo: tcgetattr obtiene la actual terminal de la información y la almacena en t. Si cmd es 1, la entrada del local de la bandera en t está configurado para no bloquear la entrada. De lo contrario, se restablece. Luego tcsetattr los cambios de la entrada estándar a la t.

    Si no restablecer la entrada estándar al final de su programa, usted tendrá problemas en su caparazón.

    • La instrucción switch es la falta de un soporte 🙂
  10. -1

    Usted puede hacer que el uso de seleccionar como sigue:

      int nfds = 0;
      fd_set readfds;
      FD_ZERO(&readfds);
      FD_SET(0, &readfds); /* set the stdin in the set of file descriptors to be selected */
      while(1)
      {
         /* Do what you want */
         int count = select(nfds, &readfds, NULL, NULL, NULL);
         if (count > 0) {
          if (FD_ISSET(0, &readfds)) {
              /* If a character was pressed then we get it and exit */
              getchar();
              break;
          }
         }
      }

    No demasiado trabajo 😀

    • Esta respuesta es incompleta. Usted necesita configurar el terminal para que no canónica modo. También nfds se debe establecer en 1.

Dejar respuesta

Please enter your comment!
Please enter your name here