2010-12-01 18 views
7

Mi aplicación de prueba esno buffering entrada estándar lectura

#include <sys/types.h> 
#include <sys/wait.h> 
#include <signal.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <fcntl.h> 

int main(int argc, char *argv[], char *envp[]) { 
    int fd[2]; 

    if(pipe(fd) < 0) { 
    printf("Can\'t create pipe\n"); 
    exit(-1); 
    } 

    pid_t fpid = fork(); 
    if (fpid == 0) { 
    close(0); 
    close(fd[1]); 
    char *s = (char *) malloc(sizeof(char)); 
    while(1) if (read(fd[0], s, 1)) printf("%i\n", *s); 
    } 
    close(fd[0]); 
    char *c = (char *) malloc(sizeof(char)); 
    while (1) { 
    if (read(0, c, 1) > 0) write(fd[1], c, 1); 
    } 
    return 0; 
} 

Quiero ver al carbón después de cada código de carbón introducido. Pero de hecho * s se imprime solo después de '\ n' en la consola. Parece que stdin (archivo con desc 0) está almacenado en el búfer. Pero la función de lectura es sin buffer, ¿no? Donde estoy equivocado

UPD: Yo uso linux.

Así que la solución es

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

int main(int argc, char *argv[], char *envp[]) { 
    int fd[2]; 

    if(pipe(fd) < 0) { 
    printf("Can\'t create pipe\n"); 
    exit(-1); 
    } 

    struct termios term, term_orig; 

    if(tcgetattr(0, &term_orig)) { 
    printf("tcgetattr failed\n"); 
    exit(-1); 
    } 

    term = term_orig; 

    term.c_lflag &= ~ICANON; 
    term.c_lflag |= ECHO; 
    term.c_cc[VMIN] = 0; 
    term.c_cc[VTIME] = 0; 

    if (tcsetattr(0, TCSANOW, &term)) { 
    printf("tcsetattr failed\n"); 
    exit(-1); 
    } 

    pid_t fpid = fork(); 
    if (fpid == 0) { 
    close(0); 
    close(fd[1]); 
    char *s = (char *) malloc(sizeof(char)); 
    while(1) if (read(fd[0], s, 1)) printf("%i\n", *s); 
    } 
    close(fd[0]); 
    char *c = (char *) malloc(sizeof(char)); 
    while (1) { 
    if (read(0, c, 1) > 0) write(fd[1], c, 1); 
    } 
    return 0; 
} 
+1

Tenga en cuenta que esto no tiene nada que ver con el almacenamiento en búfer. –

Respuesta

12

Desafortunadamente, el comportamiento que usted está buscando no es posible con el estándar ANSI C, y el modo por defecto para el terminal UNIX E/S es la línea orientado, el cual significa que siempre necesitará un carácter ingresado \n para recuperar la entrada. Tendrá que usar las funciones de E/S del terminal que le permiten programar en el modo non-canonical, de modo que cada pulsación de tecla desencadena un evento. En Linux/UNIX, puede buscar en el encabezado <termios.h> o en la biblioteca ncurses.

+0

Pero hay muchas aplicaciones que hacen esto: man, vim, etc. – Ximik

+4

@Ximik, sí, y no usan ANSI estándar C. La mayoría usa librerías externas como ncurses o termcap. –

2

Unix almacena sus caracteres tty dentro del kernel en parte para que los programas no tengan que manejar individualmente la edición de línea a menos que lo deseen.

Puede indicar al controlador tty que le proporcione los bytes inmediatamente. Hay varias bibliotecas que hacen esto un poco más fácil que usar el ioctl sin formato. Puede comenzar con termios(3).

+2

Y, lamentablemente, nadie mejoró la edición de líneas a nivel de kernel para hacerlo realmente útil ... En principio, podría ser tan agradable como 'readline'. –

3

Me parece que su solución es un poco complicada. Todavía no entiendo por qué necesita tubería y 2 procesos.

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

int main(int argc, char *argv[], char *envp[]) { 
    struct termios term, term_orig; 

    if(tcgetattr(0, &term_orig)) { 
    printf("tcgetattr failed\n"); 
    exit(-1); 
    } 

    term = term_orig; 

    term.c_lflag &= ~ICANON; 
    term.c_lflag |= ECHO; 
    term.c_cc[VMIN] = 0; 
    term.c_cc[VTIME] = 0; 

    if (tcsetattr(0, TCSANOW, &term)) { 
    printf("tcsetattr failed\n"); 
    exit(-1); 
    } 

    char ch; 
    while (1) { 
    if (read(0, &ch, 1) > 0) 
     printf(" %d\n", ch); 
    } 
    return 0; 
} 
Cuestiones relacionadas