En el siguiente código:
#include <stdio.h>
int main(void) {
int c;
while ((c=getchar())!= EOF)
putchar(c);
return 0;
}
Tengo que presionar Enterpara imprimir todas las letras con las que ingresé getchar
, pero no quiero hacer esto, lo que quiero hacer es presionar la letra e inmediatamente ver la letra que introduje repetida sin presionar Enter. Por ejemplo, si presiono la letra 'a' quiero ver otra 'a' junto a ella, y así sucesivamente:
aabbccddeeff.....
Pero cuando presiono 'a' no pasa nada, puedo escribir otras letras y la copia aparece solo cuando presiono Enter:
abcdef
abcdef
¿Cómo puedo hacer esto?
Estoy usando el comando cc -o example example.c
en Ubuntu para compilar.
Respuestas:
En un sistema Linux, puede modificar el comportamiento del terminal usando el
stty
comando. De forma predeterminada, el terminal almacenará en búfer toda la información hasta que Enterse presione, incluso antes de enviarla al programa C.Un ejemplo rápido, sucio y no particularmente portátil para cambiar el comportamiento desde el propio programa:
#include<stdio.h> #include<stdlib.h> int main(void){ int c; /* use system call to make terminal send all keystrokes directly to stdin */ system ("/bin/stty raw"); while((c=getchar())!= '.') { /* type a period to break out of the loop, since CTRL-D won't work raw */ putchar(c); } /* use system call to set terminal behaviour to more normal behaviour */ system ("/bin/stty cooked"); return 0; }
Tenga en cuenta que esto no es realmente óptimo, ya que simplemente asume que ese
stty cooked
es el comportamiento que desea cuando se cierra el programa, en lugar de verificar cuáles eran las configuraciones originales del terminal. Además, dado que todo el procesamiento especial se omite en modo sin procesar, muchas secuencias de teclas (como CTRL-C o CTRL-D ) no funcionarán realmente como usted espera sin procesarlas explícitamente en el programa.Puede tener
man stty
más control sobre el comportamiento del terminal, dependiendo exactamente de lo que desee lograr.fuente
system
es una mala idea.Esto depende de su sistema operativo, si se encuentra en un entorno similar a UNIX, el indicador ICANON está habilitado de forma predeterminada, por lo que la entrada se almacena en búfer hasta el próximo
'\n'
oEOF
. Al deshabilitar el modo canónico, obtendrás los personajes de inmediato. Esto también es posible en otras plataformas, pero no existe una solución multiplataforma sencilla.EDITAR: Veo que especificó que usa Ubuntu. Ayer publiqué algo similar, pero tenga en cuenta que esto deshabilitará muchos comportamientos predeterminados de su terminal.
#include<stdio.h> #include <termios.h> //termios, TCSANOW, ECHO, ICANON #include <unistd.h> //STDIN_FILENO int main(void){ int c; static struct termios oldt, newt; /*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; /*ICANON normally 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); /*Those new settings will be set to STDIN TCSANOW tells tcsetattr to change attributes immediately. */ tcsetattr( STDIN_FILENO, TCSANOW, &newt); /*This is your part: I choose 'e' to end input. Notice that EOF is also turned off in the non-canonical mode*/ while((c=getchar())!= 'e') putchar(c); /*restore the old settings*/ tcsetattr( STDIN_FILENO, TCSANOW, &oldt); return 0; }
Notarás que cada personaje aparece dos veces. Esto se debe a que la entrada se repite inmediatamente en la terminal y luego su programa también la devuelve
putchar()
. Si desea disociar la entrada de la salida, también debe desactivar la bandera ECHO. Puede hacer esto simplemente cambiando la línea apropiada a:fuente
getchar () es una función estándar que en muchas plataformas requiere que presione ENTER para obtener la entrada, porque la plataforma almacena la entrada hasta que se presiona esa tecla. Muchos compiladores / plataformas admiten el getch () no estándar que no se preocupa por ENTER (evita el almacenamiento en búfer de la plataforma, trata a ENTER como una tecla más).
fuente
getchar()
comienza a leer los caracteres uno por uno solo después de presionar entergetchar()
no le importa ENTER, solo procesa lo que venga a través de stdin. El almacenamiento en búfer de línea tiende a ser un comportamiento definido por el sistema operativo / terminal.getchar()
que “requiera que pulses enter”, sino del entorno.La E / S es una función del sistema operativo. En muchos casos, el sistema operativo no pasará caracteres escritos a un programa hasta que se presione ENTER. Esto permite al usuario modificar la entrada (como retroceder y volver a escribir) antes de enviarla al programa. Para la mayoría de los propósitos, esto funciona bien, presenta una interfaz consistente para el usuario y evita que el programa tenga que lidiar con esto. En algunos casos, es deseable que un programa obtenga caracteres de las teclas a medida que se presionan.
La biblioteca de C en sí se ocupa de los archivos y no se preocupa por cómo los datos ingresan al archivo de entrada. Por lo tanto, no hay forma en el propio lenguaje de obtener teclas cuando se presionan; en cambio, esto es específico de la plataforma. Como no ha especificado el sistema operativo o el compilador, no podemos buscarlo por usted.
Además, la salida estándar normalmente se almacena en búfer para mayor eficiencia. Esto lo hacen las bibliotecas de C, por lo que hay una solución en C, que es
fflush(stdout);
después de cada carácter escrito. Después de eso, si los caracteres se muestran inmediatamente depende del sistema operativo, pero todos los sistemas operativos con los que estoy familiarizado mostrarán la salida inmediatamente, por lo que normalmente no es un problema.fuente
Me gusta la respuesta de Lucas, pero me gustaría desarrollarla un poco. Hay una función incorporada en
termios.h
namedcfmakeraw()
que man describe como:cfmakeraw() sets the terminal to something like the "raw" mode of the old Version 7 terminal driver: input is available character by character, echoing is disabled, and all special processing of terminal input and output characters is disabled. [...]
Esto básicamente hace lo mismo que sugirió Lucas y más, puede ver las banderas exactas que establece en las páginas de manual : termios (3) .
Caso de uso
int c = 0; static struct termios oldTermios, newTermios; tcgetattr(STDIN_FILENO, &oldTermios); newTermios = oldTermios; cfmakeraw(&newTermios); tcsetattr(STDIN_FILENO, TCSANOW, &newTermios); c = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldTermios); switch (c) { case 113: // q printf("\n\n"); exit(0); break; case 105: // i printf("insert\n"); break; default: break;
fuente
cfmakeraw()
es una extensión no estándar y no portátil de POSIXtermios.h
y no está necesariamente disponible.Dado que está trabajando en un derivado de Unix (Ubuntu), aquí hay una forma de hacerlo: no se recomienda, pero funcionará (siempre que pueda escribir comandos con precisión):
echo "stty -g $(stty -g)" > restore-sanity stty cbreak ./your_program
Utilice la interrupción para detener el programa cuando esté aburrido de él.
Puede economizar si 'stty sane' restaura su configuración con la suficiente precisión para sus propósitos. El formato de '-g' no es portátil entre las versiones de 'stty' (por lo que lo que se genera en Solaris 10 no funcionará en Linux, o viceversa), pero el concepto funciona en todas partes. La opción 'stty sane' no está disponible universalmente, AFAIK (pero está en Linux).
fuente
Podría incluir la biblioteca 'ncurses' y usar en
getch()
lugar degetchar()
.fuente
sí, también puede hacer esto en Windows, aquí está el código a continuación, usando la biblioteca conio.h
#include <iostream> //basic input/output #include <conio.h> //provides non standard getch() function using namespace std; int main() { cout << "Password: "; string pass; while(true) { char ch = getch(); if(ch=='\r'){ //when a carriage return is found [enter] key cout << endl << "Your password is: " << pass <<endl; break; } pass+=ch; cout << "*"; } getch(); return 0; }
fuente
Este problema o pregunta surgió en una tarea en la que estoy trabajando actualmente. También depende de la entrada de la que esté obteniendo. estoy usando
para obtener entrada mientras el programa se está ejecutando, por lo que debe ser el flujo de archivos asociado con el comando.
En la máquina ubuntu que tengo que probar / apuntar, requería más que solo
system( "stty -raw" );
o
system( "stty -icanon" );
Tuve que agregar el indicador --file, así como la ruta al comando, así:
system( "/bin/stty --file=/dev/tty -icanon" );
Todo es copacetico ahora.
fuente
Este código funcionó para mí. Atención: esto no es parte de la biblioteca estándar, incluso si la mayoría de los compiladores (yo uso GCC) lo admiten.
#include <stdio.h> #include <conio.h> int main(int argc, char const *argv[]) { char a = getch(); printf("You typed a char with an ASCII value of %d, printable as '%c'\n", a, a); return 0; }
Este código detecta la primera pulsación de tecla.
fuente
En primer lugar, la entrada del terminal suele ser de línea o con búfer completo. Esto significa que el sistema operativo almacena la entrada real del terminal en un búfer. Por lo general, este búfer se descarga al programa cuando
\n
se señalizó / proporcionó festdin
. Esto es hecho por una prensa para Enter.getchar()
está al final de la cadena. No tiene capacidad para influir realmente en el proceso de almacenamiento en búfer.Abandone
getchar()
en primer lugar, si no desea utilizar llamadas al sistema específicas para cambiar el comportamiento del terminal explícitamente como se explica en las otras respuestas.Desafortunadamente, no existe una función de biblioteca estándar y con eso no hay una forma portátil de vaciar el búfer en la entrada de un solo carácter. Sin embargo, existen soluciones basadas en la implementación y no portátiles.
En Windows / MS-DOS, existen las funciones
getch()
ygetche()
en elconio.h
archivo de encabezado, que hacen exactamente lo que usted desea: leer un solo carácter sin la necesidad de esperar a que la nueva línea limpie el búfer.La principal diferencia entre
getch()
ygetche()
es quegetch()
no genera inmediatamente el carácter de entrada real en la consola, mientras que logetche()
hace. El adicional"e"
significa eco .Ejemplo:
#include <stdio.h> #include <conio.h> int main (void) { int c; while ((c = getche()) != EOF) { if (c == '\n') { break; } printf("\n"); } return 0; }
En Linux, una forma de obtener el proceso de caracteres y salida directa es el uso de los
cbreak()
yecho()
las opciones y lagetch()
erefresh()
rutinas en el ncurses-biblioteca.Tenga en cuenta que debe inicializar la llamada pantalla estándar con
initscr()
y cerrar la misma con lasendwin()
rutinas.Ejemplo:
#include <stdio.h> #include <ncurses.h> int main (void) { int c; cbreak(); echo(); initscr(); while ((c = getch()) != ERR) { if (c == '\n') { break; } printf("\n"); refresh(); } endwin(); return 0; }
Nota: Debe invocar el compilador con la
-lncurses
opción, para que el enlazador pueda buscar y encontrar la biblioteca ncurses.fuente
De forma predeterminada, la biblioteca C almacena en búfer la salida hasta que ve un retorno. Para imprimir los resultados inmediatamente, utilice
fflush
:while((c=getchar())!= EOF) { putchar(c); fflush(stdout); }
fuente