¿Cómo se lee de stdin?

1474

Estoy tratando de hacer algunos de los desafíos del código de golf , pero todos requieren que se tome la entrada stdin. ¿Cómo consigo eso en Python?

tehryan
fuente

Respuestas:

952

Podrías usar el fileinputmódulo:

import fileinput

for line in fileinput.input():
    pass

fileinput recorrerá todas las líneas en la entrada especificada como nombres de archivo dados en argumentos de línea de comandos, o la entrada estándar si no se proporcionan argumentos.

Nota: linecontendrá una nueva línea final; para eliminarlo useline.rstrip()

u0b34a0f6ae
fuente
1
@BorislavStoilov Y esta respuesta responde correctamente la pregunta: "o la entrada estándar si no se proporcionan argumentos".
Dietmar
1
La documentación indica que recurre a stdin: "Esto itera sobre las líneas de todos los archivos enumerados en sys.argv [1:], por defecto a sys.stdin si la lista está vacía. Si un nombre de archivo es '-', también se reemplaza por sys.stdin. Para especificar una lista alternativa de nombres de archivo, páselo como el primer argumento para input (). También se permite un único nombre de archivo ".
Arlo
721

Hay algunas formas de hacerlo.

  • sys.stdines un objeto similar a un archivo en el que puede llamar funciones reado readlinessi desea leer todo o si desea leer todo y dividirlo por nueva línea automáticamente. (Es necesario que import sysesto funcione).

  • Si desea solicitar al usuario una entrada, puede usarla raw_inputen Python 2.X y solo inputen Python 3.

  • Si en realidad solo desea leer las opciones de la línea de comandos, puede acceder a ellas a través de la lista sys.argv .

Probablemente encontrará que este artículo de Wikibook sobre E / S en Python también es una referencia útil.

Mark Rushakoff
fuente
445
import sys

for line in sys.stdin:
    print(line)

Tenga en cuenta que esto incluirá un carácter de nueva línea al final. Para eliminar la nueva línea al final, use line.rstrip()como dijo @brittohalloran.

usuario303110
fuente
77
line.rstrip ('\ n'), de lo contrario, eliminará todos los espacios en blanco
avp
usando este método, ¿cómo sabemos cuándo termina la secuencia de entrada? Quiero agregar una coma después de cada línea, excepto la última línea.
adicto el
Recibo: TypeError: el objeto 'FileWrapper' no es iterable.
Diego Queiroz
@avp esto no va a tratar correctamente \r\nlos finales de línea
Josch
228

Python también tiene funciones integradas input()y raw_input(). Consulte la documentación de Python en Funciones incorporadas .

Por ejemplo,

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

o

name = input("Enter your name: ")   # Python 3
Pat Notz
fuente
77
Esto lee una sola línea, que no es realmente lo que preguntó el OP. Interpreto la pregunta como "¿cómo leo un montón de líneas desde un identificador de archivo abierto hasta EOF?"
tripleee
44
El OP no está pidiendo leer la entrada desde un teclado, está pidiendo leer desde el stdin que, en una situación de concurso, generalmente se proporciona a los concursantes.
chrisfs
Esto es lo que necesitaba, Google me trajo aquí.
Curiosamente
204

Aquí está de Learning Python :

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

En Unix, puede probarlo haciendo algo como:

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

En Windows o DOS, harías:

C:\> type countlines.py | python countlines.py 
Counted 3 lines.
Eugene Yokota
fuente
44
He aquí una más eficiente de la memoria (y tal vez más rápido) manera de contar las líneas en Python: print(sum(chunk.count('\n') for chunk in iter(partial(sys.stdin.read, 1 << 15), ''))). verwc-l.py
jfs
11
El uso de cataquí es redundante. La invocación correcta para sistemas Unix es python countlines.py < countlines.py.
istepaniuk
12
"Aprender Python" es incorrecto al indicar a los usuarios que lo usen readlines(). Los objetos de archivo están destinados a ser iterados sin materializar todos los datos en la memoria.
Aaron Hall
118

¿Cómo se lee desde stdin en Python?

Estoy tratando de hacer algunos de los desafíos del código de golf, pero todos requieren que la entrada se tome de stdin. ¿Cómo consigo eso en Python?

Puedes usar:

  • sys.stdin- Un objeto similar a un archivo: llame sys.stdin.read()para leer todo.
  • input(prompt)- pásele un mensaje opcional para generar, lee desde stdin hasta la primera línea nueva, que elimina. Tendría que hacer esto repetidamente para obtener más líneas, al final de la entrada aumenta EOFError. (Probablemente no es genial para jugar al golf.) En Python 2, esto es rawinput(prompt).
  • open(0).read()- En Python 3, la función incorporada openacepta descriptores de archivo (enteros que representan recursos de E / S del sistema operativo) y 0 es el descriptor de stdin. Devuelve un objeto similar a un archivo sys.stdin, probablemente su mejor apuesta para jugar al golf. En Python 2, esto es io.open.
  • open('/dev/stdin').read()- similar a open(0), funciona en Python 2 y 3, pero no en Windows (o incluso Cygwin).
  • fileinput.input()- devuelve un iterador sobre líneas en todos los archivos enumerados sys.argv[1:], o stdin si no se proporciona. Utilizar como ''.join(fileinput.input()).

Ambos sysy fileinputdeben importarse, respectivamente, por supuesto.

sys.stdinEjemplos rápidos compatibles con Python 2 y 3, Windows, Unix

Sólo tiene que readpartir sys.stdin, por ejemplo, si los datos de tubería a la entrada estándar:

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

Podemos ver que sys.stdinestá en modo de texto predeterminado:

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

ejemplo de archivo

Digamos que tiene un archivo, inputs.txtpodemos aceptar ese archivo y volver a escribirlo:

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

Respuesta más larga

Aquí hay una demostración completa, fácilmente replicable, usando dos métodos, la función incorporada, input(uso raw_inputen Python 2) y sys.stdin. Los datos no se modifican, por lo que el procesamiento no funciona.

Para empezar, creemos un archivo para entradas:

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

Y usando el código que ya hemos visto, podemos verificar que hemos creado el archivo:

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

Aquí está la ayuda 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.

Función incorporada, input( raw_inputen Python 2)

La función incorporada inputlee desde la entrada estándar hasta una nueva línea, que se elimina (complementando print, lo que agrega una nueva línea por defecto). Esto ocurre hasta que obtiene EOF (Fin del archivo), momento en el que se eleva EOFError.

Por lo tanto, así es como puede usar inputen Python 3 (o raw_inputen Python 2) para leer desde stdin, por lo que creamos un módulo de Python que llamamos stdindemo.py:

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

Y imprimámoslo nuevamente para asegurarnos de que es como esperamos:

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

Nuevamente, inputlee hasta la nueva línea y esencialmente la elimina de la línea. printagrega una nueva línea. Entonces, mientras ambos modifican la entrada, sus modificaciones se cancelan. (Por lo tanto, son esencialmente el complemento del otro).

Y cuando inputobtiene el carácter de fin de archivo, genera EOFError, que ignoramos y luego salimos del programa.

Y en Linux / Unix, podemos canalizar desde cat:

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

O simplemente podemos redirigir el archivo desde stdin:

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

También podemos ejecutar el módulo como un script:

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

Aquí está la ayuda sobre la construcción 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

Aquí hacemos un script de demostración usando sys.stdin. La forma eficiente de iterar sobre un objeto similar a un archivo es usar el objeto similar a un archivo como iterador. El método complementario para escribir en stdout desde esta entrada es simplemente usar sys.stdout.write:

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

Imprima de nuevo para asegurarse de que se ve bien:

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

Y redirigiendo las entradas al archivo:

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

Golfed en un comando:

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

Descriptores de archivo para golf

Dado que los descriptores de archivo para stdiny stdoutson 0 y 1 respectivamente, también podemos pasarlos a openPython 3 (no a 2, y tenga en cuenta que todavía necesitamos la 'w' para escribir en stdout).

Si esto funciona en su sistema, eliminará más caracteres.

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

Python 2 también io.openhace esto, pero la importación ocupa mucho más espacio:

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

Abordar otros comentarios y respuestas

Un comentario sugiere ''.join(sys.stdin)para jugar al golf, pero en realidad es más largo que sys.stdin.read (); además, Python debe crear una lista adicional en la memoria (así es como str.joinfunciona cuando no se le da una lista), por contraste:

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

La respuesta principal sugiere:

import fileinput

for line in fileinput.input():
    pass

Pero, dado que sys.stdinimplementa la API de archivo, incluido el protocolo de iterador, es lo mismo que esto:

import sys

for line in sys.stdin:
    pass

Otra respuesta no sugiere esto. Sólo recuerde que si lo hace en un intérprete, usted tiene que hacer Ctrl- dsi estás en Linux o Mac, o Ctrl- zen Windows (después Enter) para enviar el carácter de fin de archivo en el proceso. Además, esa respuesta sugiere print(line), que agrega una '\n'al final, usar print(line, end='')en su lugar (si en Python 2, necesitará from __future__ import print_function).

El caso de uso real fileinputes para leer en una serie de archivos.

Aaron Hall
fuente
103

La respuesta propuesta por otros:

for line in sys.stdin:
  print line

es muy simple y pitónico, pero debe tenerse en cuenta que el script esperará hasta EOF antes de comenzar a iterar en las líneas de entrada.

Esto significa que tail -f error_log | myscript.pyno procesará líneas como se esperaba.

El script correcto para tal caso de uso sería:

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

    if not line:
        break

    print line

ACTUALIZACIÓN
De los comentarios se ha aclarado que en python 2 solo puede haber un búfer involucrado, por lo que terminará esperando que se llene el búfer o EOF antes de que se emita la llamada de impresión.

Massimiliano Torromeo
fuente
8
El for line in sys.stdin:patrón no espera EOF. Pero si prueba en archivos muy pequeños, las respuestas se pueden almacenar. Pruebe con más datos para ver que lee resultados intermedios.
mb.
Espero el final del archivo o el almacenamiento en búfer, cuando tomo la entrada de una secuencia cuando uso Python 2.6.6, pero con 3.1.3 no lo hago. Note print lineno se despertó en 3.1.3, pero print(line)sí.
ctrl-alt-delor
mi python 2.7.5 "para línea en sys.stdin", bloquea hasta EOF o se ha almacenado una cantidad razonable de datos. Bien para el procesamiento de flujo. No está bien para el procesamiento línea por línea o entrada del usuario.
Sean
2
Sospecho que esto está relacionado con la detección de tty en libc, por lo que cuando canaliza detecta en un shell interactivo no detecta tty, unbuffer de expect-dev es una utilidad útil que creo que inyecta una cuña a través de ld_preload por lo que is_atty devuelve verdadero (I sospecha que así es como se lo está entregando)
Mâtt Frëëman
8
@Sean: mal . for line in sys.stdin:no "bloquea hasta EOF". Hay un error de lectura anticipada en Python 2 que retrasa las líneas hasta que el búfer correspondiente esté lleno. Es un problema de almacenamiento en búfer que no está relacionado con EOF. Para solucionarlo, use for line in iter(sys.stdin.readline, ''):(use io.open()para archivos ordinarios). No lo necesitas en Python 3.
jfs
39

Esto hará eco de la entrada estándar a la salida estándar:

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

Sobre la base de todas las respuestas usando sys.stdin, también puede hacer algo como lo siguiente para leer desde un archivo de argumentos si existe al menos un argumento, y recurrir a stdin de lo contrario:

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

y úsalo como

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

o

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

o incluso

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

Eso haría que su script Python se comportara como muchos programas GNU / Unix como cat, grepy sed.

Emil Lundberg
fuente
17

argparse es una solución fácil

Ejemplo compatible con las versiones 2 y 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()

Puede ejecutar este script de muchas maneras:

1. Usando stdin

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

  o más corto reemplazando echopor cadena aquí :

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

2. Usando un argumento de nombre de archivo

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

3. Usando a stdintravés del nombre de archivo especial-

echo 'foo bar' | ./above-script.py -
olibre
fuente
Aquí hay una respuesta sobre qué hacer, si el archivo de entrada está comprimido: stackoverflow.com/a/33621549/778533 También se puede hacer add_argument('--in'y luego canalizar al script y agregarlo --in -a la línea de comando. PS inno es un muy buen nombre para una variable / atributo.
tommy.carstensen
inno es solo un mal nombre para una variable, es ilegal. args.in.read()generará un error InvalidSyntax debido a la inpalabra clave reservada. Simplemente puede cambiar el nombre para infilegustar los documentos de discusión de python do: docs.python.org/3/library/…
Ken Colton el
Gracias @ tommy.carstensen por sus comentarios, acabo de mejorar la respuesta. Feliz Navidad y Feliz Año Nuevo ;-)
olibre
14

El siguiente chip de código lo ayudará (leerá todo el bloqueo de stdin EOFen una sola cadena):

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

Estoy bastante sorprendido de que nadie haya mencionado este truco hasta ahora:

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

en python2 puede soltar la set()llamada, pero sería de cualquier manera

Uri Goren
fuente
1
¿Por qué usar readlinesesa división en líneas y luego joinotra vez? Puedes escribirprint(sys.stdin.read())
musiphil
Esto usará más memoria de la necesaria porque Python necesita construir una matriz adicional.
Harry Moreno
Bueno, en realidad no, porque writeregresa None, y el tamaño establecido nunca sería mayor que 1 ( =len(set([None])))
Uri Goren
7

Prueba esto:

import sys

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

y compruébalo con:

$ echo "Hello World" | python myFile.py
Boubakr
fuente
6

Leer desde sys.stdin, pero para leer datos binarios en Windows , debe tener mucho cuidado, ya sys.stdinque se abre en modo texto y se dañará al \r\nreemplazarlos \n.

La solución es establecer el modo en binario si se detecta Windows + Python 2, y en Python 3 usar 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
fuente
4

Uso el siguiente método, devuelve una cadena de stdin (lo uso para el análisis json). Funciona con pipe y prompt en Windows (aún no se ha probado en Linux). Cuando se le solicita, dos saltos de línea indican el final de la entrada.

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
fuente
3

El problema que tengo con la solucion

import sys

for line in sys.stdin:
    print(line)

es que si no pasa ningún dato a stdin, se bloqueará para siempre. Es por eso que me encanta esta respuesta : primero verifique si hay algunos datos en stdin y luego léala. Esto es lo que terminé haciendo:

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)
Tomás Tomecek
fuente
Sin embargo, recomendaría seriamente ocultar esta horrible condición en un método.
tiktak
1
Este método limita seriamente la aplicabilidad del programa: por ejemplo, no puede usar esto para la entrada interactiva desde el terminal, porque la entrada casi nunca estará "lista" cuando selectse llama; o también podría enfrentar problemas si stdin está conectado a un archivo en un medio lento (red, CD, cinta, etc.). Dijiste que "si no pasas ningún dato a stdin, se bloqueará para siempre". es un problema , pero yo diría que es una característica . La mayoría de los programas CLI (por ejemplo cat) funcionan de esta manera, y se espera que lo hagan. EOF es lo único en lo que debe depender para detectar el final de la entrada.
musiphil
2

Tuve algunos problemas al hacer que esto funcionara para leer sobre sockets conectados a él. Cuando el zócalo se cerró, comenzó a devolver una cadena vacía en un bucle activo. Así que esta es mi solución (que solo probé en Linux, pero espero que funcione en todos los demás sistemas)

import sys, os
sep=os.linesep

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

Entonces, si comienza a escuchar en un socket, funcionará correctamente (por ejemplo, en bash):

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

Y puede llamarlo con telnet o simplemente apuntar un navegador a localhost: 12345

estani
fuente
1

Con respecto a este:

for line in sys.stdin:

Acabo de probarlo en Python 2.7 (siguiendo la sugerencia de otra persona) para un archivo muy grande, y no lo recomiendo, precisamente por las razones mencionadas anteriormente (no pasa nada durante mucho tiempo).

Terminé con una solución un poco más pitónica (y funciona en archivos más grandes):

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

Entonces puedo ejecutar el script localmente como:

python myscript.py "0 1 2 3 4..." # can be a multi-line string or filename - any std.in input will work
szeitlin
fuente
Abrir un archivo no es leer desde stdin, como se hace la pregunta. -1
Aaron Hall
En este caso, paso sys.stdincomo argumento de línea de comandos al script.
szeitlin
1
¿Cómo podría pasar sys.stdincomo un argumento de línea de comandos al script? Los argumentos son cadenas y las secuencias son objetos en forma de archivo, no son lo mismo.
DeFazer
@DeFazer editado para mostrar cómo usarlo. Los argumentos son cadenas, sí, pero como los documentos de Python y lo mencioné en un comentario anterior, sys.stdines un objeto similar a un archivo
szeitlin
1

Para Python 3 eso sería:

# Filename e.g. cat.py
import sys

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

Esta es básicamente una forma simple de cat (1), ya que no agrega una nueva línea después de cada línea. Puede usar esto (después de haber marcado el archivo ejecutable usando chmod +x cat.py:

echo Hello | ./cat.py
AdamKalisz
fuente
0

Hay os.read(0, x) que lee xbytes de 0 que representa stdin. Esta es una lectura sin búfer, más bajo nivel que sys.stdin.read ()

Arrendajo
fuente
0

Al usar el -ccomando, como una forma complicada, en lugar de leer el stdin(y más flexible en algunos casos), también puede pasar un comando de script de shell a su comando de Python poniendo el comando de venta entre comillas dentro de un paréntesis que comienza con un $signo.

p.ej

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

Esto contará el número de líneas del archivo histórico de goldendict.

Kasramvd
fuente