web-dev-qa-db-fra.com

Comment éviter d'appuyer sur Entrée avec getchar ()

Dans le code suivant:

#include <stdio.h>

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}

Je dois appuyer Enter pour imprimer toutes les lettres que j'ai saisies avec getchar, mais je ne veux pas le faire, ce que je veux, c'est appuyer sur la lettre et voir immédiatement la lettre que j'ai introduite répétée sans appuyer sur Enter. Par exemple, si j'appuie sur la lettre "a", je veux voir un autre "a" à côté, etc.:

aabbccddeeff.....

Mais quand j'appuie sur 'a' rien ne se passe, je peux écrire d'autres lettres et la copie n'apparaît que lorsque j'appuie sur Enter:

abcdef
abcdef

Comment puis-je faire ceci?

J'utilise la commande cc -o example example.c sous Ubuntu pour la compilation.

67
javier

Sur un système Linux, vous pouvez modifier le comportement du terminal à l'aide de la commande stty. Par défaut, le terminal mettra toutes les informations en mémoire tampon jusqu’à ce que Enter est enfoncé avant même de l'envoyer au programme C.

Un exemple rapide, sale et pas particulièrement portable pour changer le comportement depuis le programme lui-même:

#include<stdio.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;
}

S'il vous plaît noter que ce n'est pas vraiment optimal, car il suppose en quelque sorte que stty cooked est le comportement souhaité lors de la fermeture du programme, plutôt que de vérifier quels étaient les paramètres d'origine du terminal. De plus, étant donné que tous les traitements spéciaux sont ignorés en mode brut, de nombreuses séquences de touches (telles que CTRL-C ou CTRL-D) ne fonctionneront pas comme prévu. sans les traiter explicitement dans le programme.

Vous pouvez man stty pour plus de contrôle sur le comportement du terminal, en fonction de ce que vous voulez réaliser.

62
goldPseudo

Cela dépend de votre système d'exploitation. Si vous êtes dans un environnement semblable à UNIX, l'indicateur ICANON est activé par défaut. Par conséquent, l'entrée est mise en mémoire tampon jusqu'à la prochaine '\n' Ou EOF. En désactivant le mode canonique, vous obtiendrez les caractères immédiatement. Ceci est également possible sur d'autres plates-formes, mais il n'existe pas de solution multi-plateforme simple.

EDIT: Je vois que vous avez spécifié que vous utilisez Ubuntu. Je viens de poster quelque chose de similaire hier, mais sachez que cela désactivera de nombreux comportements par défaut de votre 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;
}

Vous remarquerez que chaque personnage apparaît deux fois. En effet, l'entrée est renvoyée immédiatement au terminal, puis votre programme la restitue avec putchar(). Si vous souhaitez dissocier l'entrée de la sortie, vous devez également activer le drapeau ECHO. Vous pouvez le faire en modifiant simplement la ligne appropriée en:

newt.c_lflag &= ~(ICANON | ECHO); 
78
Lucas

getchar () est une fonction standard qui sur de nombreuses plates-formes nécessite d'appuyer sur ENTREE pour obtenir l'entrée, car l'entrée de la plate-forme tamponne jusqu'à ce que cette touche soit enfoncée. De nombreux compilateurs/plates-formes prennent en charge la méthode non standard getch () qui ne se soucie pas de ENTRER (contourne la mise en mémoire tampon de la plate-forme, traite ENTRÉE comme une autre clé).

7
Eric J.

I/O est une fonction du système d'exploitation. Dans de nombreux cas, le système d'exploitation ne transmet pas les caractères saisis à un programme tant que vous n'avez pas appuyé sur ENTREE. Cela permet à l'utilisateur de modifier l'entrée (telle que l'espacement arrière et le ressaisi) avant de l'envoyer au programme. Dans la plupart des cas, cela fonctionne bien, présente une interface cohérente pour l'utilisateur et évite au programme de devoir gérer cela. Dans certains cas, il est souhaitable qu'un programme récupère les caractères des touches au fur et à mesure de leur pression.

La bibliothèque C elle-même traite des fichiers et ne s'intéresse pas à la manière dont les données entrent dans le fichier d'entrée. Par conséquent, il n’ya aucun moyen dans la langue elle-même d’obtenir les touches au fur et à mesure qu’elles sont enfoncées; à la place, ceci est spécifique à la plate-forme. Puisque vous n'avez pas spécifié de système d'exploitation ou de compilateur, nous ne pouvons pas le rechercher.

De plus, la sortie standard est normalement tamponnée pour plus d’efficacité. Ceci est fait par les bibliothèques C, et donc il y a une solution C, qui consiste à fflush(stdout); après chaque caractère écrit. Ensuite, le système d’exploitation doit afficher ou non les caractères immédiatement, mais tous les systèmes d’exploitation avec lesquels je suis familier afficheront immédiatement la sortie, ce qui ne pose normalement pas de problème.

6
David Thornley

J'aime la réponse de Lucas, mais je voudrais élaborer un peu. Il y a une fonction intégrée dans termios.h Nommée cfmakeraw() qui man décrit comme:

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. [...]

En gros, cela fait la même chose que ce que Lucas a suggéré et plus encore, vous pouvez voir les indicateurs exacts qu’il définit dans les pages de manuel: termios (3) .

Cas d'utilisation

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;
4
Patrik Höggren

Puisque vous travaillez sur un dérivé d’Unix (Ubuntu), voici une façon de le faire - cela n’est pas recommandé, mais cela fonctionnera (tant que vous pourrez taper des commandes avec précision):

echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program

Utilisez interrompre pour arrêter le programme lorsque vous en avez marre.

sh restore-sanity
  • La ligne "echo" enregistre les paramètres actuels du terminal en tant que script Shell qui les restaurera.
  • La ligne 'stty' désactive la plupart des traitements spéciaux (donc Control-D par exemple) et envoie des caractères au programme dès qu’ils sont disponibles. Cela signifie que vous ne pouvez plus modifier votre saisie.
  • La ligne "sh" rétablit les paramètres d'origine de votre terminal.

Vous pouvez économiser si "stty sane" restaure vos paramètres avec suffisamment de précision pour vos besoins. Le format de '-g' n'est pas portable entre les versions de 'stty' (ainsi, ce qui est généré sous Solaris 10 ne fonctionnera pas sous Linux, ou inversement), mais le concept fonctionne partout. L'option 'stty sane' n'est pas universellement disponible, autant que je sache (mais elle est sous Linux).

4
Jonathan Leffler

oui, vous pouvez aussi le faire sur Windows, voici le code ci-dessous, en utilisant la bibliothèque 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;
}
2
djaa2807

Vous pouvez inclure la bibliothèque 'ncurses' et utiliser getch() au lieu de getchar().

2
AShelly

J'ai eu ce problème/question dans une mission sur laquelle je travaille actuellement. Cela dépend également de l’entrée à partir de laquelle vous vous inscrivez. j'utilise

/dev/tty

pour obtenir des entrées pendant l'exécution du programme, il doit donc s'agir du flux de fichiers associé à la commande.

Sur la machine Ubuntu, je dois tester/cibler, il fallait plus que

system( "stty -raw" );

ou

system( "stty -icanon" );

Je devais ajouter le drapeau --file, ainsi que le chemin d'accès à la commande, comme ceci:

system( "/bin/stty --file=/dev/tty -icanon" );

Tout est copacétique maintenant.

1
Chad Chabot