Comment lisez-vous depuis stdin?

1474

J'essaie de relever certains des défis du golf de code , mais ils nécessitent tous la saisie de l'entrée stdin. Comment puis-je obtenir cela en Python?

tehryan
la source

Réponses:

952

Vous pouvez utiliser le fileinputmodule:

import fileinput

for line in fileinput.input():
    pass

fileinput parcourra toutes les lignes de l'entrée spécifiées en tant que noms de fichiers donnés dans les arguments de ligne de commande, ou l'entrée standard si aucun argument n'est fourni.

Remarque: linecontiendra une nouvelle ligne de fin; pour l'enlever utiliserline.rstrip()

u0b34a0f6ae
la source
1
@BorislavStoilov Et cette réponse répond correctement à la question: "ou l'entrée standard si aucun argument n'est fourni".
Dietmar
1
La documentation indique qu'elle revient à stdin: "Cela itère sur les lignes de tous les fichiers répertoriés dans sys.argv [1:], par défaut à sys.stdin si la liste est vide. Si un nom de fichier est '-', il est également remplacé par sys.stdin. Pour spécifier une autre liste de noms de fichiers, passez-la comme premier argument à input (). Un seul nom de fichier est également autorisé. "
Arlo
721

Il y a plusieurs façons de le faire.

  • sys.stdinest un objet de type fichier sur lequel vous pouvez appeler des fonctions readou readlinessi vous voulez tout lire ou vous voulez tout lire et le diviser automatiquement par saut de ligne. (Vous devez pour import sysque cela fonctionne.)

  • Si vous souhaitez inviter l'utilisateur à entrer des informations, vous pouvez l'utiliser raw_inputdans Python 2.X et uniquement inputdans Python 3.

  • Si vous souhaitez simplement lire les options de ligne de commande, vous pouvez y accéder via la liste sys.argv .

Vous trouverez probablement cet article de Wikibook sur les E / S en Python pour être également une référence utile.

Mark Rushakoff
la source
445
import sys

for line in sys.stdin:
    print(line)

Notez que cela inclura un caractère de nouvelle ligne à la fin. Pour supprimer la nouvelle ligne à la fin, utilisez line.rstrip()comme l'a dit @brittohalloran.

user303110
la source
7
line.rstrip ('\ n'), sinon il supprimera tous les espaces blancs
avp
en utilisant cette méthode, comment savoir quand le flux d'entrée se termine? Je veux ajouter une virgule après chaque ligne sauf pour la dernière ligne.
accro
Je reçois: TypeError: l'objet 'FileWrapper' n'est pas itérable.
Diego Queiroz
@avp cela ne traitera pas correctement les \r\nfins de ligne
josch
228

Python a également des fonctions intégrées input()et raw_input(). Voir la documentation Python sous Fonctions intégrées .

Par exemple,

name = raw_input("Enter your name: ")   # Python 2.x

ou

name = input("Enter your name: ")   # Python 3
Pat Notz
la source
7
Cela lit une seule ligne, ce qui n'est pas vraiment ce que l'OP a demandé. J'interprète la question comme "comment lire un tas de lignes à partir d'un descripteur de fichier ouvert jusqu'à EOF?"
tripleee
4
L'OP ne demande pas de lire les entrées à partir d'un clavier, il demande de lire à partir de stdin qui, dans une situation de concours, est généralement fourni aux candidats.
chrisfs
c'est ce dont j'avais besoin, google m'a amené ici. fait intéressant, j'ai réussi à coder les balises rfid, datetime, les bases de données, mais je n'ai jamais pris la peine de lire les entrées de l'utilisateur lol
clockw0rk
204

Voici l' apprentissage de Python :

import sys
data = sys.stdin.readlines()
print "Counted", len(data), "lines."

Sur Unix, vous pouvez le tester en faisant quelque chose comme:

% cat countlines.py | python countlines.py 
Counted 3 lines.

Sous Windows ou DOS, vous feriez:

C:\> type countlines.py | python countlines.py 
Counted 3 lines.
Eugene Yokota
la source
4
Voici une plus efficace de la mémoire (et peut - être plus rapide) façon de compter les lignes en Python: print(sum(chunk.count('\n') for chunk in iter(partial(sys.stdin.read, 1 << 15), ''))). voirwc-l.py
jfs
11
L'utilisation d' catici est redondante. L'invocation correcte pour les systèmes Unix est python countlines.py < countlines.py.
istepaniuk
12
"Apprendre Python" a tort de diriger les utilisateurs à utiliser readlines(). Les objets fichier sont destinés à être itérés sans matérialiser toutes les données en mémoire.
Aaron Hall
118

Comment lisez-vous depuis stdin en Python?

J'essaie de relever certains des défis du golf de code, mais ils nécessitent tous que l'entrée soit tirée de stdin. Comment puis-je obtenir cela en Python?

Vous pouvez utiliser:

  • sys.stdin- Un objet de type fichier - appel sys.stdin.read()à tout lire.
  • input(prompt)- passez-lui une invite facultative pour la sortie, il lit de stdin jusqu'à la première nouvelle ligne, qu'il supprime. Vous devriez le faire à plusieurs reprises pour obtenir plus de lignes, à la fin de l'entrée, il déclenche EOFError. (Probablement pas génial pour le golf.) En Python 2, c'est le cas rawinput(prompt).
  • open(0).read()- En Python 3, la fonction intégrée openaccepte les descripteurs de fichiers (entiers représentant les ressources d'E / S du système d'exploitation), et 0 est le descripteur de stdin. Il renvoie un objet semblable à un fichier sys.stdin- probablement votre meilleur pari pour le golf. En Python 2, c'est le cas io.open.
  • open('/dev/stdin').read()- similaire à open(0), fonctionne sur Python 2 et 3, mais pas sur Windows (ni même Cygwin).
  • fileinput.input()- renvoie un itérateur sur les lignes de tous les fichiers répertoriés dans sys.argv[1:], ou stdin s'il n'est pas indiqué. Utilisez comme ''.join(fileinput.input()).

Les deux syset fileinputdoivent être importés, respectivement, bien sûr.

sys.stdinExemples rapides compatibles avec Python 2 et 3, Windows, Unix

Vous avez juste besoin readde sys.stdin, par exemple, si vous dirigez des données vers stdin:

$ echo foo | python -c "import sys; print(sys.stdin.read())"
foo

Nous pouvons voir que sys.stdinc'est en mode texte par défaut:

>>> import sys
>>> sys.stdin
<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>

exemple de fichier

Supposons que vous ayez un fichier, inputs.txtnous pouvons l'accepter et le réécrire:

python -c "import sys; sys.stdout.write(sys.stdin.read())" < inputs.txt

Réponse plus longue

Voici une démo complète et facilement reproductible, utilisant deux méthodes, la fonction intégrée, input(à utiliser raw_inputen Python 2), et sys.stdin. Les données ne sont pas modifiées, le traitement est donc une non-opération.

Pour commencer, créons un fichier pour les entrées:

$ python -c "print('foo\nbar\nbaz')" > inputs.txt

Et en utilisant le code que nous avons déjà vu, nous pouvons vérifier que nous avons créé le fichier:

$ python -c "import sys; sys.stdout.write(sys.stdin.read())" < inputs.txt 
foo
bar
baz

Voici l'aide sys.stdin.readde Python 3:

read(size=-1, /) method of _io.TextIOWrapper instance
    Read at most n characters from stream.

    Read from underlying buffer until we have n characters or we hit EOF.
    If n is negative or omitted, read until EOF.

Fonction intégrée, input( raw_inputen Python 2)

La fonction intégrée inputlit depuis l'entrée standard jusqu'à une nouvelle ligne, qui est supprimée (complément print, qui ajoute une nouvelle ligne par défaut.) Cela se produit jusqu'à ce qu'elle obtienne EOF (End Of File), moment auquel elle augmente EOFError.

Ainsi, voici comment vous pouvez utiliser inputen Python 3 (ou raw_inputen Python 2) pour lire à partir de stdin - nous créons donc un module Python que nous appelons stdindemo.py:

$ python -c "print('try:\n    while True:\n        print(input())\nexcept EOFError:\n    pass')" > stdindemo.py 

Et imprimons-le de nouveau pour nous assurer qu'il est comme prévu:

$ python -c "import sys; sys.stdout.write(sys.stdin.read())" < stdindemo.py 
try:
    while True:
        print(input())
except EOFError:
    pass

Encore une fois, inputlit jusqu'à la nouvelle ligne et la retire essentiellement de la ligne. printajoute une nouvelle ligne. Ainsi, alors qu'ils modifient tous les deux l'entrée, leurs modifications s'annulent. (Ils sont donc essentiellement complémentaires les uns des autres.)

Et quand inputobtient le caractère de fin de fichier, il déclenche EOFError, que nous ignorons puis quittons le programme.

Et sous Linux / Unix, nous pouvons diriger depuis cat:

$ cat inputs.txt | python -m stdindemo
foo
bar
baz

Ou nous pouvons simplement rediriger le fichier depuis stdin:

$ python -m stdindemo < inputs.txt 
foo
bar
baz

Nous pouvons également exécuter le module en tant que script:

$ python stdindemo.py < inputs.txt 
foo
bar
baz

Voici l'aide sur le module intégré inputde Python 3:

input(prompt=None, /)
    Read a string from standard input.  The trailing newline is stripped.

    The prompt string, if given, is printed to standard output without a
    trailing newline before reading input.

    If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
    On *nix systems, readline is used if available.

sys.stdin

Ici, nous faisons un script de démonstration en utilisant sys.stdin. Le moyen efficace d'itérer sur un objet de type fichier est d'utiliser l'objet de type fichier comme itérateur. La méthode complémentaire pour écrire sur stdout à partir de cette entrée consiste simplement à utiliser sys.stdout.write:

$ python -c "print('import sys\nfor line in sys.stdin:\n    sys.stdout.write(line)')" > stdindemo2.py

Imprimez-le pour vous assurer qu'il a l'air correct:

$ python -c "import sys; sys.stdout.write(sys.stdin.read())" < stdindemo2.py 
import sys
for line in sys.stdin:
    sys.stdout.write(line)

Et rediriger les entrées dans le fichier:

$ python -m stdindemo2 < inputs.txt
foo
bar
baz

Joué dans une commande:

$ python -c "import sys; sys.stdout.write(sys.stdin.read())" < inputs.txt
foo
bar
baz

Descripteurs de fichiers pour le golf

Étant donné que les descripteurs de fichiers pour stdinet stdoutsont respectivement 0 et 1, nous pouvons également les transmettre à openPython 3 (pas 2, et notons que nous avons toujours besoin du «w» pour écrire sur stdout).

Si cela fonctionne sur votre système, cela supprimera plus de caractères.

$ python -c "open(1,'w').write(open(0).read())" < inputs.txt
baz
bar
foo

Python 2 le io.openfait également, mais l'importation prend beaucoup plus de place:

$ python -c "from io import open; open(1,'w').write(open(0).read())" < inputs.txt 
foo
bar
baz

Répondre à d'autres commentaires et réponses

Un commentaire suggère ''.join(sys.stdin)pour le golf, mais c'est en fait plus long que sys.stdin.read () - en plus Python doit créer une liste supplémentaire en mémoire (c'est comme ça que ça str.joinmarche quand on ne lui donne pas de liste) - pour le contraste:

''.join(sys.stdin)
sys.stdin.read()

La première réponse suggère:

import fileinput

for line in fileinput.input():
    pass

Mais, depuis sys.stdinimplémente l'API de fichier, y compris le protocole itérateur, c'est exactement la même chose que ceci:

import sys

for line in sys.stdin:
    pass

Une autre réponse ne suggère cela. N'oubliez pas que si vous le faites dans un interpréteur, vous devrez faire Ctrl- dsi vous êtes sous Linux ou Mac, ou Ctrl- zsous Windows (après Enter) pour envoyer le caractère de fin de fichier au processus. En outre, cette réponse suggère print(line)- ce qui ajoute un '\n'à la fin - à la print(line, end='')place (si en Python 2, vous en aurez besoin from __future__ import print_function).

Le vrai cas d'utilisation pour fileinputest de lire une série de fichiers.

Aaron Hall
la source
103

La réponse proposée par d'autres:

for line in sys.stdin:
  print line

est très simple et pythonique, mais il faut noter que le script attendra EOF avant de commencer à itérer sur les lignes d'entrée.

Cela signifie que tail -f error_log | myscript.pyles lignes ne seront pas traitées comme prévu.

Le script correct pour un tel cas d'utilisation serait:

while 1:
    try:
        line = sys.stdin.readline()
    except KeyboardInterrupt:
        break

    if not line:
        break

    print line

MISE
À JOUR D'après les commentaires, il a été effacé que sur python 2 seulement, il peut y avoir un tampon impliqué, de sorte que vous finissez par attendre que le tampon se remplisse ou EOF avant que l'appel d'impression ne soit émis.

Massimiliano Torromeo
la source
8
Le for line in sys.stdin:motif n'attend pas EOF. Mais si vous testez sur de très petits fichiers, les réponses peuvent être mises en mémoire tampon. Testez avec plus de données pour voir qu'il lit les résultats intermédiaires.
mb.
J'attends la fin du fichier ou la mise en mémoire tampon, lors de la prise d'entrée d'un flux lors de l'utilisation de python 2.6.6, mais avec 3.1.3 je ne le fais pas. Remarque print linene s'est pas réveillé en 3.1.3, mais le print(line)fait.
ctrl-alt-delor
mon python 2.7.5 "pour la ligne dans sys.stdin", bloque jusqu'à EOF ou une quantité raisonnable de données a été mise en mémoire tampon. Amende pour le traitement de flux. Pas très bien pour le traitement ligne par ligne ou l'entrée utilisateur.
Sean
2
Je soupçonne que cela est lié à la détection de tty dans libc, donc lorsque vous canalisez, il détecte sur un shell interactif, il ne détecte aucun tty, le tampon de expect-dev est un utilitaire pratique qui, je crois, injecte une cale via ld_preload donc is_atty renvoie true (I soupçonne que c'est comme ça qu'il le remet)
Mâtt Frëëman
8
@Sean: faux . for line in sys.stdin:ne "bloque pas jusqu'à EOF". Il existe un bogue de lecture anticipée dans Python 2 qui retarde les lignes jusqu'à ce que le tampon correspondant soit plein. Il s'agit d'un problème de mise en mémoire tampon qui n'est pas lié à l'EOF. Pour contourner ce problème, utilisez for line in iter(sys.stdin.readline, ''):(utiliser io.open()pour les fichiers ordinaires). Vous n'en avez pas besoin en Python 3.
jfs
39

Cela fera écho de l'entrée standard à la sortie standard:

import sys
line = sys.stdin.readline()
while line:
    print line,
    line = sys.stdin.readline()
rlib
la source
31

En s'appuyant sur toutes les réponses à l'aide sys.stdin, vous pouvez également faire quelque chose comme ce qui suit pour lire à partir d'un fichier d'arguments s'il existe au moins un argument, et revenir à stdin sinon:

import sys
f = open(sys.argv[1]) if len(sys.argv) > 1 else sys.stdin    
for line in f:
#     Do your stuff

et l'utiliser comme

$ python do-my-stuff.py infile.txt

ou

$ cat infile.txt | python do-my-stuff.py

ou même

$ python do-my-stuff.py < infile.txt

Cela ferait en sorte que votre script Python se comporte comme de nombreux programmes GNU / Unix tels que cat, grepet sed.

Emil Lundberg
la source
17

argparse est une solution simple

Exemple compatible avec les versions 2 et 3 de Python:

#!/usr/bin/python

import argparse
import sys

parser = argparse.ArgumentParser()

parser.add_argument('infile',
                    default=sys.stdin,
                    type=argparse.FileType('r'),
                    nargs='?')

args = parser.parse_args()

data = args.infile.read()

Vous pouvez exécuter ce script de plusieurs manières:

1. Utilisation stdin

echo 'foo bar' | ./above-script.py

  ou plus court en remplaçant echopar ici la chaîne :

./above-script.py <<< 'foo bar'

2. Utilisation d'un argument de nom de fichier

echo 'foo bar' > my-file.data
./above-script.py my-file.data

3. Utilisation stdinvia le nom de fichier spécial-

echo 'foo bar' | ./above-script.py -
olibre
la source
Voici une réponse sur ce qu'il faut faire, si le fichier d'entrée est compressé: stackoverflow.com/a/33621549/778533 On peut également faire add_argument('--in', puis diriger vers le script et l'ajouter --in -à la ligne de commande. PS inn'est pas un très bon nom pour une variable / un attribut.
tommy.carstensen
inn'est pas seulement un mauvais nom pour une variable, c'est illégal. args.in.read()générera une erreur InvalidSyntax en raison du inmot clé réservé. Peut simplement renommer pour infileaimer les documents python argparse: docs.python.org/3/library/…
Ken Colton
Merci @ tommy.carstensen pour vos commentaires, je viens d'améliorer la réponse. Joyeux Noël et bonne année ;-)
olibre
14

La puce de code suivante vous aidera (elle lira tout le blocage de stdin EOFdans une seule chaîne):

import sys
input_str = sys.stdin.read()
print input_str.split()
Chandan Kumar
la source
8

Je suis assez étonné que personne n'ait mentionné ce hack jusqu'à présent:

python -c "import sys; set(map(sys.stdout.write,sys.stdin))"

en python2, vous pouvez abandonner l' set()appel, mais il s'exprimerait de toute façon

Uri Goren
la source
1
Pourquoi utiliser readlinesce fractionnement en lignes, puis à joinnouveau? Vous pouvez simplement écrireprint(sys.stdin.read())
musiphil
Cela utilisera plus de mémoire que nécessaire car python doit construire un tableau supplémentaire.
Harry Moreno
Eh bien, pas vraiment, car les writeretours None, et la taille définie ne seraient jamais supérieurs à 1 ( =len(set([None])))
Uri Goren
7

Essaye ça:

import sys

print sys.stdin.read().upper()

et vérifiez-le avec:

$ echo "Hello World" | python myFile.py
Boubakr
la source
7

Vous pouvez lire à partir de stdin puis stocker les entrées dans des "données" comme suit:

data = ""
for line in sys.stdin:
    data += line
Wei
la source
La même chose peut être faite avec data = sys.stdin.read(), sans problème de concaténations répétées de chaînes.
musiphil
6

Lisez à partir de sys.stdin, mais pour lire les données binaires sur Windows , vous devez être très prudent, car sys.stdinil est ouvert en mode texte et il sera corrompu \r\nen les remplaçant par \n.

La solution consiste à définir le mode sur binaire si Windows + Python 2 est détecté et sur Python 3 à utiliser sys.stdin.buffer.

import sys

PY3K = sys.version_info >= (3, 0)

if PY3K:
    source = sys.stdin.buffer
else:
    # Python 2 on Windows opens sys.stdin in text mode, and
    # binary data that read from it becomes corrupted on \r\n
    if sys.platform == "win32":
        # set sys.stdin to binary mode
        import os, msvcrt
        msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
    source = sys.stdin

b = source.read()
anatoly techtonik
la source
4

J'utilise la méthode suivante, elle retourne une chaîne de stdin (je l'utilise pour l'analyse json). Il fonctionne avec pipe et prompt sur Windows (pas encore testé sur Linux). Lorsque vous y êtes invité, deux sauts de ligne indiquent la fin de la saisie.

def get_from_stdin():

  lb = 0
  stdin = ''

  for line in sys.stdin:
    if line == "\n":
        lb += 1
        if lb == 2:
            break
    else:
        lb = 0
        stdin += line

  return stdin
Bouni
la source
3

Le problème que j'ai avec la solution

import sys

for line in sys.stdin:
    print(line)

est que si vous ne transmettez aucune donnée à stdin, il se bloquera pour toujours. C'est pourquoi j'aime cette réponse : vérifiez s'il y a d'abord des données sur stdin, puis lisez-les. C'est ce que j'ai fini par faire:

import sys
import select

# select(files to read from, files to write to, magic, timeout)
# timeout=0.0 is essential b/c we want to know the asnwer right away
if select.select([sys.stdin], [], [], 0.0)[0]:
    help_file_fragment = sys.stdin.read()
else:
    print("No data passed to stdin", file=sys.stderr)
    sys.exit(2)
Tomas Tomecek
la source
Je recommanderais sérieusement de cacher cette condition hideuse dans une méthode.
tiktak
1
Cette méthode limite sérieusement l'applicabilité du programme: par exemple, vous ne pouvez pas l'utiliser pour une entrée interactive à partir du terminal, car l'entrée ne sera presque jamais "prête" lorsqu'elle selectest appelée; ou vous pourriez également rencontrer des problèmes si stdin est connecté à un fichier sur un support lent (réseau, CD, bande, etc.). Vous avez dit que "si vous ne transmettez aucune donnée à stdin, elle se bloquera pour toujours". est un problème , mais je dirais que c'est une fonctionnalité . La plupart des programmes CLI (par exemple cat) fonctionnent de cette façon, et ils devraient le faire. EOF est la seule chose sur laquelle vous devez compter pour détecter la fin de l'entrée.
musiphil
2

J'ai eu quelques problèmes lorsque cela fonctionnait pour la lecture sur les sockets raccordées. Lorsque le socket a été fermé, il a commencé à renvoyer une chaîne vide dans une boucle active. C'est donc ma solution (que je n'ai testé que sous Linux, mais j'espère que cela fonctionne dans tous les autres systèmes)

import sys, os
sep=os.linesep

while sep == os.linesep:
    data = sys.stdin.readline()               
    sep = data[-len(os.linesep):]
    print '> "%s"' % data.strip()

Donc, si vous commencez à écouter sur un socket, cela fonctionnera correctement (par exemple en bash):

while :; do nc -l 12345 | python test.py ; done

Et vous pouvez l'appeler avec telnet ou simplement pointer un navigateur vers localhost: 12345

estani
la source
1

Concernant ceci:

for line in sys.stdin:

Je viens de l'essayer sur python 2.7 (suite à la suggestion de quelqu'un d'autre) pour un très gros fichier, et je ne le recommande pas, précisément pour les raisons mentionnées ci-dessus (rien ne se passe depuis longtemps).

Je me suis retrouvé avec une solution légèrement plus pythonique (et cela fonctionne sur des fichiers plus gros):

with open(sys.argv[1], 'r') as f:
    for line in f:

Ensuite, je peux exécuter le script localement en tant que:

python myscript.py "0 1 2 3 4..." # can be a multi-line string or filename - any std.in input will work
szeitlin
la source
L'ouverture d'un fichier ne lit pas depuis stdin, comme le demande la question. -1
Aaron Hall
Dans ce cas, je passe sys.stdincomme argument de ligne de commande au script.
szeitlin
1
Comment pouvez-vous passer sys.stdincomme argument de ligne de commande au script? Les arguments sont des chaînes et les flux sont des objets de type fichier, ils ne sont pas identiques.
DeFazer
@DeFazer a été modifié pour montrer comment l'utiliser. Les arguments sont des chaînes, oui, mais comme les documents python et moi l'avons mentionné dans un commentaire précédent ci-dessus, sys.stdinest un objet de type fichier
szeitlin
1

Pour Python 3, ce serait:

# Filename e.g. cat.py
import sys

for line in sys.stdin:
    print(line, end="")

Il s'agit essentiellement d'une forme simple de cat (1), car il n'ajoute pas de nouvelle ligne après chaque ligne. Vous pouvez l'utiliser (après avoir marqué le fichier exécutable en utilisant chmod +x cat.pypar exemple:

echo Hello | ./cat.py
AdamKalisz
la source
0

Il y a os.read(0, x) qui lit xbytes à partir de 0 qui représente stdin. Il s'agit d'une lecture sans tampon, de niveau plus bas que sys.stdin.read ()

Geai
la source
0

Lorsque vous utilisez la -ccommande, de manière délicate, au lieu de lire le stdin(et plus flexible dans certains cas), vous pouvez également passer une commande de script shell à votre commande python en plaçant la commande sell entre guillemets entre parenthèses commencée par $signe.

par exemple

python3 -c "import sys; print(len(sys.argv[1].split('\n')))" "$(cat ~/.goldendict/history)"

Cela comptera le nombre de lignes du fichier historique de Goldendict.

Kasramvd
la source