Come leggi da stdin?


1474

Sto cercando di fare alcune delle sfide del golf del codice , ma tutte richiedono l'input da cui trarre stdin. Come posso ottenerlo in Python?

Risposte:


952

È possibile utilizzare il fileinputmodulo:

import fileinput

for line in fileinput.input():
    pass

fileinput scorrerà in sequenza tutte le righe dell'input specificato come nome file indicato negli argomenti della riga di comando o l'input standard se non viene fornito alcun argomento.

Nota: lineconterrà una nuova riga finale; per rimuoverlo utilizzareline.rstrip()


1
@BorislavStoilov E questa risposta risponde correttamente alla domanda: "o l'input standard se non vengono forniti argomenti".
Dietmar

1
La documentazione afferma che ricade su stdin: "Questo scorre le righe di tutti i file elencati in sys.argv [1:], impostando di default su sys.stdin se l'elenco è vuoto. Se un nome file è '-', viene anche sostituito di sys.stdin. Per specificare un elenco alternativo di nomi di file, passalo come primo argomento a input (). È consentito anche un singolo nome di file. "
Arlo,

721

Ci sono alcuni modi per farlo.

  • sys.stdinè un oggetto simile a un file su cui è possibile chiamare funzioni reado readlinesse si desidera leggere tutto o si desidera leggere tutto e dividerlo automaticamente per newline. (È necessario import sysper farlo funzionare.)

  • Se si desidera richiedere all'utente l'input, è possibile utilizzare raw_inputin Python 2.X e solo inputin Python 3.

  • Se in realtà vuoi solo leggere le opzioni della riga di comando, puoi accedervi tramite l' elenco sys.argv .

Probabilmente troverai questo articolo di Wikibook su I / O in Python come utile riferimento.


445
import sys

for line in sys.stdin:
    print(line)

Si noti che questo includerà un carattere di nuova riga alla fine. Per rimuovere la nuova riga alla fine, utilizzare line.rstrip()come ha detto @brittohalloran.


7
line.rstrip ('\ n'), altrimenti eliminerà tutti gli spazi bianchi
avp

utilizzando questo metodo, come facciamo a sapere quando termina il flusso di input? Voglio aggiungere una virgola dopo ogni riga tranne l'ultima riga.
dipendente il

Ricevo: TypeError: l'oggetto 'FileWrapper' non è iterabile.
Diego Queiroz,

@avp questo non tratterà correttamente le \r\nterminazioni di riga
josch

228

Python ha anche funzioni integrate input()e raw_input(). Consulta la documentazione di Python in Funzioni integrate .

Per esempio,

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

o

name = input("Enter your name: ")   # Python 3

7
Questo legge una sola riga, che non è proprio quello che l'OP ha chiesto. Interpreto la domanda come "come posso leggere un mucchio di righe da un handle di file aperto fino a EOF?"
Tripleee,

4
L'OP non sta chiedendo di leggere l'input da una tastiera, sta chiedendo di leggere dallo stdin che in una situazione di contest viene solitamente fornito ai concorrenti.
chrisfs,

questo è ciò di cui avevo bisogno, google mi ha portato qui. è interessante notare che sono riuscito a codificare tag rfid, datetime, database, ma non mi sono mai preoccupato di leggere l'input dell'utente lol
clockw0rk

204

Ecco da Learning Python :

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

Su Unix, potresti provarlo facendo qualcosa del tipo:

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

Su Windows o DOS, dovresti:

C:\> type countlines.py | python countlines.py 
Counted 3 lines.

4
Ecco un altro efficiente della memoria (e forse più veloce) modo per contare le righe in Python: print(sum(chunk.count('\n') for chunk in iter(partial(sys.stdin.read, 1 << 15), ''))). vediwc-l.py
jfs

11
L'uso di catqui è ridondante. L'invocazione corretta per i sistemi Unix è python countlines.py < countlines.py.
istepaniuk,

12
"Imparare Python" è sbagliato nel dire agli utenti di usare readlines(). Gli oggetti file devono essere ripetuti senza materializzare tutti i dati in memoria.
Aaron Hall

118

Come leggi dallo stdin in Python?

Sto provando a fare alcune delle sfide del golf del codice, ma tutte richiedono che l'input sia preso dallo stdin. Come posso ottenerlo in Python?

Puoi usare:

  • sys.stdin- Un oggetto simile a un file: chiama sys.stdin.read()per leggere tutto.
  • input(prompt)- passare un prompt opzionale per l'output, legge da stdin fino alla prima nuova riga, che si spoglia. Dovresti farlo ripetutamente per ottenere più righe, alla fine dell'input genera EOFError. (Probabilmente non eccezionale per il golf.) In Python 2, questo è rawinput(prompt).
  • open(0).read()- In Python 3, la funzione integrata openaccetta i descrittori di file (numeri interi che rappresentano risorse IO del sistema operativo) e 0 è il descrittore di stdin. Restituisce un oggetto simile a un file, sys.stdinprobabilmente la soluzione migliore per giocare a golf. In Python 2, questo è io.open.
  • open('/dev/stdin').read()- simile a open(0), funziona su Python 2 e 3, ma non su Windows (o persino Cygwin).
  • fileinput.input()- restituisce un iteratore su righe in tutti i file elencati in sys.argv[1:], oppure stdin se non specificato. Usa come ''.join(fileinput.input()).

Entrambi syse fileinputdevono essere importati, ovviamente, ovviamente.

sys.stdinEsempi rapidi compatibili con Python 2 e 3, Windows, Unix

Hai solo bisogno di readda sys.stdin, per esempio, se si i dati di tubo a STDIN:

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

Possiamo vedere che sys.stdinè in modalità testo predefinita:

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

esempio di file

Supponiamo che tu abbia un file, inputs.txtpossiamo accettarlo e riscriverlo:

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

Risposta più lunga

Ecco una demo completa, facilmente replicabile, usando due metodi, la funzione integrata, input(usare raw_inputin Python 2) e sys.stdin. I dati non sono modificati, pertanto l'elaborazione non è operativa.

Per cominciare, creiamo un file per gli input:

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

E usando il codice che abbiamo già visto, possiamo verificare che abbiamo creato il file:

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

Ecco l'aiuto sys.stdin.readdi 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.

Funzione integrata, input( raw_inputin Python 2)

La funzione incorporata inputlegge dallo standard input fino a una nuova riga, che viene rimossa (complementare print, che aggiunge una nuova riga per impostazione predefinita). Ciò si verifica fino a quando non ottiene EOF (End Of File), a quel punto si alza EOFError.

Quindi, ecco come puoi usare inputin Python 3 (o raw_inputin Python 2) per leggere da stdin - quindi creiamo un modulo Python che chiamiamo stdindemo.py:

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

E stampiamolo indietro per assicurarci che sia come ci aspettiamo:

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

Ancora una volta, inputlegge fino alla nuova riga e sostanzialmente lo toglie dalla riga. printaggiunge una nuova riga. Quindi, mentre entrambi modificano l'input, le loro modifiche si annullano. (Quindi sono essenzialmente il complemento reciproco.)

E quando inputottiene il carattere di fine file, genera EOFError, che ignoriamo e quindi usciamo dal programma.

E su Linux / Unix, possiamo pipe da cat:

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

Oppure possiamo semplicemente reindirizzare il file da stdin:

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

Possiamo anche eseguire il modulo come script:

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

Ecco l'aiuto sull'integrato inputda 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

Qui facciamo uno script demo usando sys.stdin. Il modo più efficiente per scorrere su un oggetto simile a un file è utilizzare l'oggetto simile a un file come iteratore. Il metodo complementare per scrivere su stdout da questo input è semplicemente usare sys.stdout.write:

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

Stampalo di nuovo per assicurarti che appaia bene:

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

E reindirizzando gli input nel file:

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

Lanciato in un comando:

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

Descrittori di file per il golf

Poiché i descrittori di file per stdine stdoutsono rispettivamente 0 e 1, possiamo anche passarli a openin Python 3 (non 2, e notiamo che abbiamo ancora bisogno della 'w' per scrivere su stdout).

Se funziona sul tuo sistema, eliminerà più caratteri.

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

Anche Python 2 io.openfa questo, ma l'importazione occupa molto più spazio:

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

Affrontare altri commenti e risposte

Un commento suggerisce ''.join(sys.stdin)per il golf, ma in realtà è più lungo di sys.stdin.read () - inoltre Python deve creare un elenco aggiuntivo in memoria (è così che str.joinfunziona quando non viene fornito un elenco) - per contrasto:

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

La risposta migliore suggerisce:

import fileinput

for line in fileinput.input():
    pass

Ma, poiché sys.stdinimplementa l'API di file, incluso il protocollo iteratore, è esattamente lo stesso di questo:

import sys

for line in sys.stdin:
    pass

Un'altra risposta non suggerire questo. Ricorda solo che se lo fai in un interprete, dovrai farlo Ctrl- dse sei su Linux o Mac, o Ctrl- zsu Windows (dopo Enter) per inviare il carattere di fine file al processo. Inoltre, quella risposta suggerisce print(line)- che aggiunge un '\n'alla fine - usa print(line, end='')invece (se in Python 2, avrai bisogno from __future__ import print_function).

Il vero caso d'uso fileinputè per la lettura in una serie di file.


103

La risposta proposta da altri:

for line in sys.stdin:
  print line

è molto semplice e pitonico, ma va notato che lo script attenderà EOF prima di iniziare a iterare sulle righe di input.

Ciò significa che tail -f error_log | myscript.pynon elaborerà le linee come previsto.

Lo script corretto per tale caso d'uso sarebbe:

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

    if not line:
        break

    print line

AGGIORNAMENTO
Dai commenti è stato chiarito che su Python 2 potrebbe essere coinvolto solo il buffering, quindi si finisce per attendere che il buffer si riempia o EOF prima che venga emessa la chiamata di stampa.


8
Lo for line in sys.stdin:schema no attende EOF. Ma se esegui test su file di dimensioni molto ridotte, le risposte potrebbero essere memorizzate nel buffer. Prova con più dati per vedere che legge risultati intermedi.
mb.

Aspetto la fine del file o il buffering, quando ricevo input da uno stream quando utilizzo python 2.6.6, ma con 3.1.3 non lo faccio. Nota print linenon si sveglia in 3.1.3, ma lo print(line)fa.
ctrl-alt-delor,

my python 2.7.5 "for line in sys.stdin", si blocca fino a quando EOF o una quantità ragionevole di dati è stata memorizzata nel buffer. Va bene per l'elaborazione del flusso. Non va bene per l'elaborazione riga per riga o l'input dell'utente.
Sean,

2
Ho il sospetto che questo sia legato al rilevamento di tty in libc, quindi quando lo pipe rileva su una shell interattiva non rileva alcun tty, il buffuffer da prevedono-dev è un utile util che credo inietti uno shim tramite ld_preload quindi is_atty restituisce vero (I sospetto che è così che lo sta consegnando)
Mâtt Frëëman

8
@Sean: sbagliato . for line in sys.stdin:non "blocca fino a EOF". Esiste un bug read-ahead in Python 2 che ritarda le righe fino a quando il buffer corrispondente è pieno. Si tratta di un problema di buffering non correlato a EOF. Per risolvere il problema, utilizzare for line in iter(sys.stdin.readline, ''):(utilizzare io.open()per file ordinari). Non ne hai bisogno in Python 3.
jfs

39

Questo farà eco all'input standard all'output standard:

import sys
line = sys.stdin.readline()
while line:
    print line,
    line = sys.stdin.readline()

31

Sulla base di tutte le risposte che usano sys.stdin, puoi anche fare qualcosa di simile al seguente per leggere da un file di argomenti se esiste almeno un argomento, e ricorrere allo stdin altrimenti:

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

e usalo come uno dei due

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

o

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

o anche

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

Ciò renderebbe il tuo script Python comportarsi come molti programmi GNU / Unix come cat, grepe sed.


17

argparse è una soluzione facile

Esempio compatibile con entrambe le versioni Python 2 e 3:

#!/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()

Puoi eseguire questo script in molti modi:

1. Utilizzo stdin

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

  o più breve sostituendo echocon qui stringa :

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

2. Utilizzo di un argomento per il nome file

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

3. Utilizzo stdintramite il nome file speciale-

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

Ecco una risposta su cosa fare, se il file di input è compresso: stackoverflow.com/a/33621549/778533 Si può anche fare add_argument('--in'e poi reindirizzare allo script e aggiungere --in -alla riga di comando. PS innon è un nome molto valido per una variabile / attributo.
tommy.carstensen,

innon è solo un brutto nome per una variabile, è illegale. args.in.read()genererà l'errore InvalidSyntax a causa della inparola chiave riservata. Può semplicemente rinominare infilecome fanno i documenti di Python Argparse: docs.python.org/3/library/…
Ken Colton,

Grazie @ tommy.carstensen per il tuo feedback, ho appena migliorato la risposta. Buon Natale e Felice Anno Nuovo ;-)
olibre

14

Il seguente chip di codice ti aiuterà (leggerà tutto il blocco stdin EOFin, in una stringa):

import sys
input_str = sys.stdin.read()
print input_str.split()

8

Sono piuttosto sorpreso che nessuno abbia menzionato questo hack finora:

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

in python2 puoi abbandonare la set()chiamata, ma sarebbe una parola in entrambi i modi


1
Perché usare readlinesquella divisione in linee e poi di joinnuovo? Puoi semplicemente scrivereprint(sys.stdin.read())
musiphil,

Questo utilizzerà più memoria del necessario perché python deve creare un array aggiuntivo.
Harry Moreno,

Bene, non proprio, perché writeritorna Nonee la dimensione impostata non sarebbe mai maggiore di 1 ( =len(set([None])))
Uri Goren

7

Prova questo:

import sys

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

e controllalo con:

$ echo "Hello World" | python myFile.py

7

È possibile leggere da stdin e quindi memorizzare gli input in "dati" come segue:

data = ""
for line in sys.stdin:
    data += line


La stessa cosa può essere fatta con data = sys.stdin.read(), senza il problema di ripetute concatenazioni di stringhe.
musiphil,

6

Leggi da sys.stdin, ma per leggere i dati binari su Windows , devi essere molto attento, perché sys.stdinè aperto in modalità testo e corromperà \r\nsostituendoli con \n.

La soluzione è impostare la modalità su binario se viene rilevato Windows + Python 2 e su Python 3 utilizzare 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()

4

Uso il seguente metodo, restituisce una stringa da stdin (la uso per l'analisi json). Funziona con pipe e prompt su Windows (non ancora testato su Linux). Quando richiesto, due interruzioni di riga indicano la fine dell'input.

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

3

Il problema che ho con la soluzione

import sys

for line in sys.stdin:
    print(line)

è che se non passi alcun dato a stdin, si bloccherà per sempre. Ecco perché adoro questa risposta : controlla prima che ci siano alcuni dati su stdin e poi leggili. Questo è quello che ho finito per fare:

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)

Consiglio vivamente di nascondere questa orribile condizione se in un metodo.
Tiktak,

1
Questo metodo limita seriamente l'applicabilità del programma: ad esempio, non è possibile utilizzarlo per l'input interattivo dal terminale, poiché l'input non sarà quasi mai "pronto" quando selectviene chiamato; oppure potresti anche riscontrare problemi se stdin è collegato a un file su un supporto lento (rete, CD, nastro, ecc.). Hai detto che "se non passi alcun dato a stdin, si bloccherà per sempre". è un problema , ma direi che è una caratteristica . La maggior parte dei programmi CLI (ad es. cat) Funzionano in questo modo e sono previsti. EOF è l'unica cosa da cui dovresti dipendere per rilevare la fine dell'input.
musiphil,

2

Ho avuto dei problemi quando riuscivo a farlo funzionare per leggere su socket collegati ad esso. Quando il socket è stato chiuso, ha iniziato a restituire una stringa vuota in un loop attivo. Quindi questa è la mia soluzione (che ho testato solo su Linux, ma spero che funzioni in tutti gli altri sistemi)

import sys, os
sep=os.linesep

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

Quindi se inizi ad ascoltare su un socket funzionerà correttamente (es. In bash):

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

E puoi chiamarlo con telnet o semplicemente puntare un browser a localhost: 12345


1

A riguardo:

for line in sys.stdin:

L'ho appena provato su Python 2.7 (seguendo il suggerimento di qualcun altro) per un file molto grande, e non lo consiglio, proprio per i motivi sopra menzionati (non succede nulla per molto tempo).

Ho finito con una soluzione leggermente più pitonica (e funziona su file più grandi):

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

Quindi posso eseguire lo script localmente come:

python myscript.py "0 1 2 3 4..." # can be a multi-line string or filename - any std.in input will work

L'apertura di un file non sta leggendo da stdin, come fa la domanda. -1
Aaron Hall

In questo caso sto passando sys.stdincome argomento da riga di comando allo script.
szeitlin,

1
Come hai potuto passare sys.stdincome argomento della riga di comando allo script? Gli argomenti sono stringhe e gli stream sono oggetti simili a file, non sono gli stessi.
DeFazer,

@DeFazer modificato per mostrare come usarlo. Gli argomenti sono stringhe, sì, ma come i documenti di Python e di cui ho parlato in un precedente commento sopra, sys.stdinè un oggetto simile a un file
szeitlin,

1

Per Python 3 sarebbe:

# Filename e.g. cat.py
import sys

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

Questa è fondamentalmente una semplice forma di gatto (1), poiché non aggiunge una nuova riga dopo ogni riga. È possibile utilizzare questo (dopo aver contrassegnato il file eseguibile utilizzando chmod +x cat.pycome:

echo Hello | ./cat.py

0

C'è os.read(0, x) che legge xbyte da 0 che rappresenta lo stdin. Questa è una lettura senza buffer, di livello più basso rispetto a sys.stdin.read ()


0

Quando si utilizza il -ccomando, come un modo complicato, invece di leggere il stdin(e più flessibile in alcuni casi) è possibile passare un comando di script shell anche al comando python inserendo il comando sell tra virgolette all'interno di una parentesi iniziata da $segno.

per esempio

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

Questo conterà il numero di righe dal file cronologico di goldendict.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.