web-dev-qa-db-fra.com

sys.stdin.readline () lit sans invite, en retournant 'rien entre les deux'

J'ai une fonction qui exécute les tâches suivantes (entre autres): 

userinput = stdin.readline()
betAmount = int(userinput)

Est supposé prendre un entier en entrée de stdin en tant que chaîne et le convertir en un entier.

Cependant, lorsque j'appelle la fonction, elle ne renvoie qu'un seul caractère de nouvelle ligne (elle n'attend même pas que je saisisse quoi que ce soit).

Plus tôt dans le programme, je reçois des informations sous la forme de:

stdin.read(1)

capturer un seul personnage.

Cela pourrait-il avoir quelque chose à voir avec cela? Est-ce que j'écris en quelque sorte un caractère de nouvelle ligne sur la prochaine ligne de stdin?

Comment puis-je réparer cela?

15
lightandlight

stdin.read(1) lit un caractère de stdin. S'il y avait plus d'un caractère à lire à ce moment-là (par exemple, la nouvelle ligne qui suivait le caractère qui avait été lu), alors ce ou ces caractères seraient toujours dans la mémoire tampon en attente de la prochaine read() ou readline()

Par exemple, donné rd.py:

from sys import stdin

x = stdin.read(1)
userinput = stdin.readline()
betAmount = int(userinput)
print ("x=",x)
print ("userinput=",userinput)
print ("betAmount=",betAmount)

... si j'exécute ce script comme suit (j'ai saisi le 234):

C:\>python rd.py
234
x= 2
userinput= 34

betAmount= 34

... donc le 2 est pris en premier, laissant le 34 et le caractère de fin de ligne suivant à la place de la readline().

Je suggérerais de résoudre le problème en utilisant readline() plutôt que read() dans la plupart des cas.

21
Simon

La réponse de Simon et celle de Volcano expliquent ensemble ce que vous faites de travers et Simon explique comment vous pouvez y remédier en modifiant votre interface.

Mais si vous avez vraiment besoin de lire un caractère, puis de lire une ligne, vous pouvez le faire. Ce n'est pas anodin, et c'est différent de Windows par rapport à tout le reste.

Il existe en réalité trois cas: un tty Unix, une invite Windows DOS ou un fichier normal (fichier/canal redirigé) sur l’une ou l’autre des plateformes. Et vous devez les gérer différemment.

Tout d’abord, pour vérifier si stdin est un tty (versions Windows et Unix), il suffit d’appeler sys.stdin.isatty(). Cette partie est multi-plateforme.

Pour les non-tty, c'est facile. Cela peut en fait fonctionner. Si ce n'est pas le cas, vous pouvez simplement lire à partir de l'objet sans tampon situé sous sys.stdin. En Python 3, cela signifie simplement sys.stdin.buffer.raw.read(1) et sys.stdin.buffer.raw.readline(). Cependant, cela vous donnera des octets codés, plutôt que des chaînes, vous devrez donc appeler .decode(sys.stdin.decoding) sur les résultats; vous pouvez envelopper tout cela dans une fonction.

Cependant, pour le cas tty sous Windows, l’entrée sera toujours protégée en ligne même sur le tampon brut. La seule solution consiste à utiliser les fonctions Console I/O au lieu des E/S de fichier normales. Donc, au lieu de stdin.read(1), vous faites msvcrt.getwch().

Pour le cas tty sous Unix, vous devez définir le terminal en mode brut au lieu du mode de discipline de ligne habituel. Une fois que vous avez fait cela, vous pouvez utiliser la même fonction sys.stdin.buffer.read(1), etc., et cela fonctionnera. Si vous êtes prêt à le faire de manière permanente (jusqu'à la fin de votre script), rien de plus simple, avec la fonction tty.setraw . Si vous souhaitez revenir au mode discipline de ligne plus tard, vous devez utiliser le module termios . Cela semble effrayant, mais si vous ne conservez que les résultats de termios.tcgetattr(sys.stdin.fileno()) avant d'appeler setraw, puis termios.tcsetattr(sys.stdin.fileno(), TCSAFLUSH, stash), vous n'avez pas à apprendre ce que signifient tous ces bits fastidieux.

Sur les deux plates-formes, la console de mixage E/S et le mode terminal brut sont pénibles. Vous ne pouvez certainement pas utiliser le tampon sys.stdin si vous avez déjà lu une lecture brute sur console /; vous ne pouvez utiliser que sys.stdin.buffer.raw. Vous pouvez toujours remplacer readline en lisant caractère par caractère jusqu'à ce que vous obteniez une nouvelle ligne… mais si l'utilisateur essaie de modifier son entrée en utilisant un retour arrière, des flèches, des touches de commande de style emacs, etc. des touches, que vous ne voulez pas traiter.

11
abarnert
stdin.read(1)

ne reviendra pas lorsque vous appuierez sur un caractère - il attendra '\ n'. Le problème est que le deuxième caractère est mis en mémoire tampon dans l'entrée standard et dès que vous appelez une autre entrée - il reviendra immédiatement car il obtient son entrée dans la mémoire tampon.

3
volcano

Si vous n'avez besoin que d'un seul caractère et que vous ne souhaitez pas conserver d'éléments dans le tampon, vous pouvez simplement lire une ligne entière et supprimer tout ce qui n'est pas nécessaire.

Remplacer:

stdin.read(1)

avec

stdin.readline().strip()[:1]

Cela lira une ligne, supprimera les espaces et les nouvelles lignes et conservera simplement le premier caractère.

2
ale5000

Essaye ça ...

import sys
buffer = []
while True:
    userinput = sys.stdin.readline().rstrip('\n')
    if userinput == 'quit':
        break
    else:
        buffer.append(userinput)
0
Reuben