¿Cómo evitar presionar Enter con getchar () para leer solo un carácter?

80

En el siguiente código:

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:

Pero cuando presiono 'a' no pasa nada, puedo escribir otras letras y la copia aparece solo cuando presiono Enter:

¿Cómo puedo hacer esto?

Estoy usando el comando cc -o example example.cen Ubuntu para compilar.

Javier
fuente
5
esto depende en gran medida de cómo su sistema operativo y su terminal procesan la entrada. si especifica su entorno operativo, probablemente obtendrá mejores respuestas.
goldPseudo
La respuesta proporcionada en cplusplus.com/forum/articles/19975 funciona en Windows que usa WinAPI.

Respuestas:

72

En un sistema Linux, puede modificar el comportamiento del terminal usando el sttycomando. 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:

Tenga en cuenta que esto no es realmente óptimo, ya que simplemente asume que ese stty cookedes 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 sttymás control sobre el comportamiento del terminal, dependiendo exactamente de lo que desee lograr.

goldPseudo
fuente
Usar systemes una mala idea.
Sapphire_Brick
Demasiado crudo ... Estaba buscando una solución que me permitiera no esperar a ENTER. Esta solución frena todas las funcionalidades del terminal de entrada.
ABC
93

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'o EOF. 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.

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:

Lucas
fuente
9
+1 Aunque tbh, probablemente este no sea el nivel de código con el que el póster original se siente cómodo;)
Andomar
2
¡Excelente! ¡Eso es lo que estaba buscando!
Denilson Sá Maia
Gracias por la ayuda junto con los comentarios detallados, resolvió mi problema.
kaminsknator
Es increíble que algo tan básico sea tan difícil de lograr y no sea portátil.
Pedro77
12

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).

Eric J.
fuente
1
+1 correcto, getchar()comienza a leer los caracteres uno por uno solo después de presionar enter
Andomar
32
getchar()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.
goldPseudo
1
@goldPseudo: Verdadero, pero casi todas las plataformas almacenan la entrada en búfer, por lo que este es el comportamiento que verá en la mayoría de los casos prácticos. getch (), donde sea compatible, administrará o ignorará el almacenamiento en búfer. Sé que algunas implementaciones antiguas de DOS evitaban el almacenamiento en búfer del sistema operativo para interactuar directamente con el hardware. Otras implementaciones pueden vaciar stdin para obtener el mismo comportamiento.
Eric J.
10
No obstante, no se trata de getchar()que “requiera que pulses enter”, sino del entorno.
Benji XVI
1
Muy conciso y fácil de entender. Otras respuestas mejor calificadas simplemente arrojan abrumadores detalles técnicos innecesarios.
mzoz
6

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.

David Thornley
fuente
6

Me gusta la respuesta de Lucas, pero me gustaría desarrollarla un poco. Hay una función incorporada en termios.hnamed cfmakeraw()que man describe como:

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

luego brillos
fuente
Tenga en cuenta que cfmakeraw()es una extensión no estándar y no portátil de POSIXtermios.h y no está necesariamente disponible.
Andrew Henle
4

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):

Utilice la interrupción para detener el programa cuando esté aburrido de él.

  • La línea 'echo' guarda la configuración actual del terminal como un script de shell que la restaurará.
  • La línea 'stty' desactiva la mayor parte del procesamiento especial (por lo que Control-D no tiene ningún efecto, por ejemplo) y envía caracteres al programa tan pronto como están disponibles. Significa que ya no puede editar su escritura.
  • La línea 'sh' restablece la configuración original del terminal.

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).

Jonathan Leffler
fuente
2

Podría incluir la biblioteca 'ncurses' y usar en getch()lugar de getchar().

AShelly
fuente
1

sí, también puede hacer esto en Windows, aquí está el código a continuación, usando la biblioteca conio.h

djaa2807
fuente
6
La pregunta está etiquetada con c , no c ++
Spikatrix
@CoolGuy ¿Y qué? ¿Por qué ayudaría a alguien si alguien abriera exactamente la misma pregunta, pero cambiara la etiqueta de c a c ++?
Andreas Haferburg
@AndreasHaferburg Porque no tendríamos respuestas en C ++ aquí, especialmente las que no están mal escritas, guiño guiño .
Sapphire_Brick
1

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

o

Tuve que agregar el indicador --file, así como la ruta al comando, así:

Todo es copacetico ahora.

Chad Chabot
fuente
2
tenga en cuenta que esta solución depende del sistema operativo.
Ottavio Campana
0

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.

Este código detecta la primera pulsación de tecla.

Firefnix
fuente
0

" ¿Cómo evitar presionar Entercon getchar()? "

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 \nse señalizó / proporcionó fe stdin. 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.


" ¿Cómo puedo hacer esto? "

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()y getche()en el conio.harchivo 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()y getche()es que getch()no genera inmediatamente el carácter de entrada real en la consola, mientras que lo getche()hace. El adicional "e"significa eco .

Ejemplo:


En Linux, una forma de obtener el proceso de caracteres y salida directa es el uso de los cbreak()y echo()las opciones y la getch()erefresh() rutinas en el ncurses-biblioteca.

Tenga en cuenta que debe inicializar la llamada pantalla estándar con initscr()y cerrar la misma con las endwin()rutinas.

Ejemplo:


Nota: Debe invocar el compilador con la -lncursesopción, para que el enlazador pueda buscar y encontrar la biblioteca ncurses.

RobertS apoya a Monica Cellio
fuente
-1

De forma predeterminada, la biblioteca C almacena en búfer la salida hasta que ve un retorno. Para imprimir los resultados inmediatamente, utilice fflush:

Andomar
fuente
2
Sin embargo, la producción es parte del problema. El interrogador desea que el programa lea las teclas a medida que se presionan e imprima inmediatamente en la pantalla. Eso es tanto un problema de entrada como de salida.
David Thornley
es decir, fflush solo descarga la salida, pero getchar aún no regresará después de presionar la tecla (las teclas se almacenarán en búfer).
NickSoft