Comprobando el buffer stdin si está vacío

Estoy intentando leer un carácter numérico con carácter, pero no sé si el búfer estándar está vacío o no.

Mi primera solución fue buscar el carácter ‘\ n’ en el búfer estándar, pero esto no es bueno si escribo varios números separados con ” (espacio en blanco).

¿Cómo puedo saber si en el búfer estándar tengo caracteres o no?

No necesito hacerlo en C y ser portátil.

Hay varias soutions:

sondee o seleccione con un tiempo de espera de 0: estos volverían de inmediato y el resultado es -1 con errno EAGAIN si no hay datos disponibles o el número de descriptores con datos (uno, ya que solo está verificando stdin).

ioctl es una razor suiza de usar descriptores. La solicitud que necesita es I_NREAD :

 if (ioctl(0, I_NREAD, &n) == 0 && n > 0) // we have exactly n bytes to read 

Sin embargo, la solución correcta es leer todo lo que tienes (usando scanf ) como una línea, luego procesar el resultado, y esto funciona suficientemente bien con sscanf :

 char buf[80]; // large enough scanf("%79s", buf); // read everything we have in stdin if (sscanf(buf, "%d", &number) == 1) // we have a number 

… siempre y cuando manejes correctamente la re-lectura, cadenas que son más largas que tu búfer y otras complicaciones de la vida real.

Edición: se eliminó feof ya que en realidad se usa para otras cosas.

Me inspiré en esto como se menciona en la publicación de @ stek29 en esta página y preparé un ejemplo simple como sigue:

 #include  #include  #include  int main(void) { fd_set readfds; FD_ZERO(&readfds); struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; char message[50]; while(1) { FD_SET(STDIN_FILENO, &readfds); if (select(1, &readfds, NULL, NULL, &timeout)) { scanf("%s", message); printf("Message: %s\n", message); } printf("...\n"); sleep(1); } return(0); } 

Para cualquier persona que venga aquí desde Google, una solución fácil de select para verificar la vacuidad stdin :

 fd_set readfds; FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); fd_set savefds = readfds; struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; int chr; int sel_rv = select(1, &readfds, NULL, NULL, &timeout); if (sel_rv > 0) { puts("Input:"); while ((chr = getchar()) != EOF) putchar(chr); } else if (sel_rv == -1) { perror("select failed"); } readfds = savefds; 

Necesita unistd.h , stdlib.h y stdio.h .

La explicación se puede encontrar aquí .

UPD: Gracias DrBeco por darse cuenta de que seleccionar devuelve -1 en error: se agregó el manejo de errores.

En realidad, seleccione devoluciones:

  • el número de descriptores listos que están contenidos en los conjuntos de descriptores
  • 0 si el tiempo límite expira
  • -1 si se produjo un error (errno se establecería)

Hay muchas maneras de verificar si la stdin tiene entrada disponible. Los más portátiles son, en ese orden: select , fcntl y poll .

Aquí algunos fragmentos de cómo hacerlo, caso por caso.

 #include  /* same old */ #include  /* same old */ #include  /* struct timeval for select() */ #include  /* select() */ #include  /* poll() */ #include  /* FIONREAD ioctl() */ #include  /* tcgetattr() and tcsetattr() */ #include  /* fnctl() */ #define BUFF 256 int chkin_select(void); int chkin_poll(void); int chkin_ioctl(void); int chkin_fcntl(void); int chkin_termios(void); /* Simple loops to test varios options of non-blocking test for stdin */ int main(void) { char sin[BUFF]="r"; printf("\nType 'q' to advance\nTesting select()\n"); while(sin[0]++ != 'q') { while(!chkin_select()) { printf("nothing to read on select()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput select(): %s\n", sin); } printf("\nType 'q' to advance\nTesting poll()\n"); while(sin[0]++ != 'q') { while(!chkin_poll()) { printf("nothing to read poll()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput poll(): %s\n", sin); } printf("\nType 'q' to advance\nTesting ioctl()\n"); while(sin[0]++ != 'q') { while(!chkin_ioctl()) { printf("nothing to read ioctl()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput ioctl(): %s\n", sin); } printf("\nType 'q' to advance\nTesting fcntl()\n"); while(sin[0]++ != 'q') { while(!chkin_fcntl()) { printf("nothing to read fcntl()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput fcntl: %s\n", sin); } printf("\nType 'q' to advance\nTesting termios()\n"); while(sin[0]++ != 'q') { while(!chkin_termios()) { printf("nothing to read termios()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput termios: %s\n", sin); } return EXIT_SUCCESS; } /* select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (eg, input possible). A file descriptor is considered ready if it is possible to perform a corresponding I/O operation (eg, read(2) without blocking, or a sufficiently small write(2)). */ int chkin_select(void) { fd_set rd; struct timeval tv={0}; int ret; FD_ZERO(&rd); FD_SET(STDIN_FILENO, &rd); ret=select(1, &rd, NULL, NULL, &tv); return (ret>0); } /* poll() performs a similar task to select(2): it waits for one of a set of file descriptors to become ready to perform I/O. The set of file descriptors to be monitored is specified in the fds argument, which is an array of structures of the following form: struct pollfd { int fd; // file descriptor // short events; // requested events // short revents; // returned events // }; The caller should specify the number of items in the fds array in nfds. */ int chkin_poll(void) { int ret; struct pollfd pfd[1] = {0}; pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; ret = poll(pfd, 1, 0); return (ret>0); } /* The ioctl(2) call for terminals and serial ports accepts many possible command arguments. Most require a third argument, of varying type, here called argp or arg. Use of ioctl makes for nonportable programs. Use the POSIX interface described in termios(3) whenever possible. */ int chkin_ioctl(void) { int n; ioctl(STDIN_FILENO, FIONREAD, &n); return (n>0); } /* fcntl() performs one of the operations described below on the open file descriptor fd. The operation is determined by cmd. fcntl() can take an optional third argument. Whether or not this argument is required is determined by cmd. The required argument type is indicated in parentheses after each cmd name (in most cases, the required type is int, and we identify the argument using the name arg), or void is specified if the argument is not required. Certain of the operations below are supported only since a particular Linux kernel version. The preferred method of checking whether the host kernel supports a particular operation is to invoke fcntl() with the desired cmd value and then test whether the call failed with EINVAL, indicating that the kernel does not recognize this value. */ int chkin_fcntl(void) { int flag, ch; flag = fcntl(STDIN_FILENO, F_GETFL, 0); /* save old flags */ fcntl(STDIN_FILENO, F_SETFL, flag|O_NONBLOCK); /* set non-block */ ch = ungetc(getc(stdin), stdin); fcntl(STDIN_FILENO, F_SETFL, flag); /* return old state */ return (ch!=EOF); } /* The termios functions describe a general terminal interface that is provided to control asynchronous communications ports. This function doesn't wait for '\n' to return! */ int chkin_termios(void) { struct termios old, new; int ch; tcgetattr(STDIN_FILENO, &old); /* save settings */ new = old; new.c_lflag &= ~ICANON; /* non-canonical mode: inputs by char, not lines */ new.c_cc[VMIN] = 0; /* wait for no bytes at all */ new.c_cc[VTIME] = 0; /* timeout */ tcsetattr(STDIN_FILENO, TCSANOW, &new); /* new settings */ ch = ungetc(getc(stdin), stdin); /* check by reading and puking it back */ tcsetattr(STDIN_FILENO, TCSANOW, &old); /* restre old settings */ return (ch!=EOF); } 

Intenta evitar ioctl y termios , son demasiado específicos o de muy bajo nivel. Además, no puedes usar feof de manera significativa con stdin o cualquier FIFO para esa materia. Puede garantizar la posición del puntero, y si lo intenta con ftell o fseek obtendrá un error ( perror a perror ).


Referencias: