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?
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?
Réponses:
Vous pouvez utiliser le fileinput
module:
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: line
contiendra une nouvelle ligne de fin; pour l'enlever utiliserline.rstrip()
Il y a plusieurs façons de le faire.
sys.stdin
est un objet de type fichier sur lequel vous pouvez appeler des fonctions read
ou readlines
si vous voulez tout lire ou vous voulez tout lire et le diviser automatiquement par saut de ligne. (Vous devez pour import sys
que cela fonctionne.)
Si vous souhaitez inviter l'utilisateur à entrer des informations, vous pouvez l'utiliser raw_input
dans Python 2.X et uniquement input
dans 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.
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.
\r\n
fins de ligne
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
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.
print(sum(chunk.count('\n') for chunk in iter(partial(sys.stdin.read, 1 << 15), '')))
. voirwc-l.py
cat
ici est redondante. L'invocation correcte pour les systèmes Unix est python countlines.py < countlines.py
.
readlines()
. Les objets fichier sont destinés à être itérés sans matérialiser toutes les données en mémoire.
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 open
accepte 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 sys
et fileinput
doivent être importés, respectivement, bien sûr.
sys.stdin
Exemples rapides compatibles avec Python 2 et 3, Windows, UnixVous avez juste besoin read
de 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.stdin
c'est en mode texte par défaut:
>>> import sys
>>> sys.stdin
<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>
Supposons que vous ayez un fichier, inputs.txt
nous pouvons l'accepter et le réécrire:
python -c "import sys; sys.stdout.write(sys.stdin.read())" < inputs.txt
Voici une démo complète et facilement reproductible, utilisant deux méthodes, la fonction intégrée, input
(à utiliser raw_input
en 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.read
de 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.
input
( raw_input
en Python 2)La fonction intégrée input
lit 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 input
en Python 3 (ou raw_input
en 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, input
lit jusqu'à la nouvelle ligne et la retire essentiellement de la ligne. print
ajoute 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 input
obtient 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é input
de 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
Étant donné que les descripteurs de fichiers pour stdin
et stdout
sont respectivement 0 et 1, nous pouvons également les transmettre à open
Python 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.open
fait é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
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.join
marche 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.stdin
implé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 fileinput
est de lire une série de fichiers.
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.py
les 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.
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.
print line
ne s'est pas réveillé en 3.1.3, mais le print(line)
fait.
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.
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
, grep
et sed
.
argparse
est une solution simpleExemple 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 echo
par 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 stdin
via le nom de fichier spécial-
echo 'foo bar' | ./above-script.py -
add_argument('--in'
, puis diriger vers le script et l'ajouter --in -
à la ligne de commande. PS in
n'est pas un très bon nom pour une variable / un attribut.
in
n'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 in
mot clé réservé. Peut simplement renommer pour infile
aimer les documents python argparse: docs.python.org/3/library/…
La puce de code suivante vous aidera (elle lira tout le blocage de stdin EOF
dans une seule chaîne):
import sys
input_str = sys.stdin.read()
print input_str.split()
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
readlines
ce fractionnement en lignes, puis à join
nouveau? Vous pouvez simplement écrireprint(sys.stdin.read())
write
retours None
, et la taille définie ne seraient jamais supérieurs à 1 ( =len(set([None]))
)
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
data = sys.stdin.read()
, sans problème de concaténations répétées de chaînes.
Lisez à partir de sys.stdin
, mais pour lire les données binaires sur Windows , vous devez être très prudent, car sys.stdin
il est ouvert en mode texte et il sera corrompu \r\n
en 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()
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
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)
select
est 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.
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
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
sys.stdin
comme argument de ligne de commande au script.
sys.stdin
comme 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.
sys.stdin
est un objet de type fichier
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.py
par exemple:
echo Hello | ./cat.py
Lorsque vous utilisez la -c
commande, 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.