Como você lê o stdin?

1473

Estou tentando fazer alguns dos desafios do código de golfe , mas todos exigem que a entrada seja retirada stdin. Como faço para obtê-lo no Python?

tehryan
fonte

Respostas:

951

Você pode usar o fileinputmódulo:

import fileinput

for line in fileinput.input():
    pass

fileinput percorrerá todas as linhas da entrada especificada como nomes de arquivo fornecidos nos argumentos da linha de comandos ou a entrada padrão se nenhum argumento for fornecido.

Nota: lineconterá uma nova linha à direita; para removê-lo useline.rstrip()

u0b34a0f6ae
fonte
1
@BorislavStoilov E esta resposta responde corretamente à pergunta: "ou a entrada padrão se nenhum argumento for fornecido".
Dietmar
1
A documentação declara que recorre ao stdin: "Isso itera sobre as linhas de todos os arquivos listados em sys.argv [1:], padronizando para sys.stdin se a lista estiver vazia. Se um nome de arquivo for '-', ele também será substituído por sys.stdin. Para especificar uma lista alternativa de nomes de arquivos, passe-o como o primeiro argumento para input (). Um único nome de arquivo também é permitido. "
Arlo
721

Existem algumas maneiras de fazer isso.

  • sys.stdiné um objeto parecido com um arquivo no qual você pode chamar funções readou readlinesse deseja ler tudo ou se deseja ler tudo e dividi-lo por nova linha automaticamente. (Você precisa que import sysisso funcione.)

  • Se você deseja solicitar uma entrada ao usuário, pode usá-lo raw_inputno Python 2.X e apenas inputno Python 3.

  • Se você realmente quiser apenas ler as opções da linha de comando, poderá acessá-las através da lista sys.argv .

Você provavelmente encontrará este artigo do Wikibook sobre E / S em Python como uma referência útil também.

Mark Rushakoff
fonte
445
import sys

for line in sys.stdin:
    print(line)

Observe que isso incluirá um caractere de nova linha no final. Para remover a nova linha no final, use line.rstrip()como @brittohalloran disse.

user303110
fonte
7
line.rstrip ('\ n'), caso contrário, eliminará todos os espaços em branco
avp
Usando esse método, como sabemos quando o fluxo de entrada termina? Quero adicionar vírgula após cada linha exceder a última linha.
Addict #
Eu recebo: TypeError: O objeto 'FileWrapper' não é iterável.
Diego Queiroz
@avp isso não irá lidar corretamente com \r\nfinais de linha
Josch
228

O Python também possui funções internas input()e raw_input(). Veja a documentação do Python em Funções internas .

Por exemplo,

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

ou

name = input("Enter your name: ")   # Python 3
Pat Notz
fonte
7
Isso lê uma única linha, que não é exatamente o que o OP perguntou. Interpreto a pergunta como "como leio várias linhas de um identificador de arquivo aberto até o EOF?"
Tripleee
4
O OP não está pedindo para ler a entrada de um teclado, ele está pedindo para ler a partir de stdin que, em uma situação de competição, geralmente é fornecido aos competidores.
Chrisfs
isso é o que eu precisava, o google me trouxe aqui. Curiosamente eu consegui etiquetas RFID código, data e hora, bancos de dados, mas nunca se preocupou em ler a entrada do usuário lol
clockw0rk
204

Aqui está do Learning Python :

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

No Unix, você pode testá-lo fazendo algo como:

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

No Windows ou DOS, você faria:

C:\> type countlines.py | python countlines.py 
Counted 3 lines.
Eugene Yokota
fonte
4
Aqui está uma memória mais eficiente (e talvez mais rápido) maneira de contar linhas em Python: print(sum(chunk.count('\n') for chunk in iter(partial(sys.stdin.read, 1 << 15), ''))). Vejawc-l.py
jfs
11
O uso cataqui é redundante. A chamada correta para sistemas Unix é python countlines.py < countlines.py.
Istepaniuk 17/11/2015
12
"Aprender Python" está errado ao direcionar os usuários a usar readlines(). Objetos de arquivo devem ser iterados sem materializar todos os dados na memória.
Aaron Hall
118

Como você lê o stdin em Python?

Estou tentando fazer alguns dos desafios do código de golfe, mas todos exigem que a entrada seja retirada do stdin. Como faço para obtê-lo no Python?

Você pode usar:

  • sys.stdin- Um objeto parecido com um arquivo - ligue sys.stdin.read()para ler tudo.
  • input(prompt)- passe um prompt opcional para a saída, ele lê do stdin até a primeira nova linha, que é removida. Você teria que fazer isso repetidamente para obter mais linhas; no final da entrada, gera EOFError. (Provavelmente não é bom para jogar golfe.) No Python 2, é isso rawinput(prompt).
  • open(0).read()- No Python 3, a função interna openaceita descritores de arquivo (números inteiros que representam recursos de E / S do sistema operacional) e 0 é o descritor de stdin. Ele retorna um objeto parecido com um arquivo como sys.stdin- provavelmente sua melhor aposta no golfe. No Python 2, é isso io.open.
  • open('/dev/stdin').read()- semelhante a open(0), funciona em Python 2 e 3, mas não no Windows (ou mesmo no Cygwin).
  • fileinput.input()- retorna um iterador sobre as linhas em todos os arquivos listados em sys.argv[1:], ou stdin, se não for fornecido. Use como ''.join(fileinput.input()).

Ambos syse fileinputdevem ser importados, respectivamente, é claro.

sys.stdinExemplos rápidos compatíveis com Python 2 e 3, Windows, Unix

Você só precisa readde sys.stdin, por exemplo, se os dados de tubulação para stdin:

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

Podemos ver que sys.stdinestá no modo de texto padrão:

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

exemplo de arquivo

Digamos que você tenha um arquivo, inputs.txtpodemos aceitá-lo e escrevê-lo novamente:

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

Resposta mais longa

Aqui está uma demonstração completa e facilmente replicável, usando dois métodos, a função embutida input(uso raw_inputno Python 2) e sys.stdin. Os dados não são modificados, portanto, o processamento é uma não operação.

Para começar, vamos criar um arquivo para entradas:

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

E usando o código que já vimos, podemos verificar se criamos o arquivo:

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

Aqui está a ajuda sys.stdin.readdo 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.

Função interna input( raw_inputem Python 2)

A função incorporada inputlê da entrada padrão até uma nova linha, que é removida (complementando print, que adiciona uma nova linha por padrão.) Isso ocorre até que receba EOF (Fim do arquivo), momento em que aumenta EOFError.

Assim, aqui está como você pode usar inputno Python 3 (ou raw_inputno Python 2) para ler a partir de stdin - então criamos um módulo Python que chamamos stdindemo.py:

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

E vamos imprimi-lo novamente para garantir que seja como esperamos:

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

Novamente, inputlê até a nova linha e retira-a essencialmente da linha. printadiciona uma nova linha. Portanto, enquanto ambos modificam a entrada, suas modificações são canceladas. (Portanto, eles são essencialmente o complemento um do outro.)

E quando inputobtém o caractere de fim de arquivo, gera EOFError, que ignoramos e saímos do programa.

E no Linux / Unix, podemos canalizar do gato:

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

Ou podemos apenas redirecionar o arquivo do stdin:

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

Também podemos executar o módulo como um script:

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

Aqui está a ajuda sobre o built-in inputdo 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

Aqui nós fazemos um script de demonstração usando sys.stdin. A maneira eficiente de iterar sobre um objeto semelhante a arquivo é usar o objeto semelhante a arquivo como um iterador. O método complementar para escrever no stdout a partir desta entrada é simplesmente usar sys.stdout.write:

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

Imprima-o novamente para garantir a aparência correta:

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

E redirecionando as entradas para o arquivo:

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

Golfed em um comando:

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

Descritores de arquivo para golfe

Como os descritores de arquivo para stdine stdoutsão 0 e 1, respectivamente, também podemos transmiti-los para o openPython 3 (não 2, e observe que ainda precisamos do 'w' para escrever no stdout).

Se isso funcionar no seu sistema, ele eliminará mais caracteres.

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

O Python 2 também io.openfaz isso, mas a importação ocupa muito mais espaço:

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

Abordando outros comentários e respostas

Um comentário sugere ''.join(sys.stdin)para jogar golfe, mas na verdade é mais longo que sys.stdin.read () - além disso, o Python deve criar uma lista extra na memória (é assim que str.joinfunciona quando não é fornecida uma lista) - para contraste:

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

A resposta principal sugere:

import fileinput

for line in fileinput.input():
    pass

Mas, como sys.stdinimplementa a API do arquivo, incluindo o protocolo do iterador, é o mesmo:

import sys

for line in sys.stdin:
    pass

Outra resposta que sugerem isso. Lembre-se de que, se você fizer isso em um intérprete, precisará fazer isso Ctrl- dse estiver no Linux ou Mac ou Ctrl- zno Windows (depois Enter) para enviar o caractere de fim de arquivo ao processo. Além disso, essa resposta sugere print(line)- que adiciona um '\n'ao final - use print(line, end='')(em Python 2, você precisará from __future__ import print_function).

O caso de uso real para fileinputé para ler em uma série de arquivos.

Aaron Hall
fonte
103

A resposta proposta por outros:

for line in sys.stdin:
  print line

é muito simples e pitônico, mas deve-se notar que o script aguardará até o EOF antes de começar a iterar nas linhas de entrada.

Isso significa que tail -f error_log | myscript.pynão processará as linhas conforme o esperado.

O script correto para esse caso de uso seria:

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

    if not line:
        break

    print line

ATUALIZAÇÃO
A partir dos comentários, foi esclarecido que somente no python 2 pode haver buffer envolvido, para que você espere que o buffer seja preenchido ou EOF antes da emissão da chamada de impressão.

Massimiliano Torromeo
fonte
8
O for line in sys.stdin:padrão não espera pelo EOF. Mas se você testar em arquivos muito pequenos, as respostas poderão ficar em buffer. Teste com mais dados para ver se ele lê resultados intermediários.
mb.
Eu fico aguardando o fim do arquivo ou o buffer, ao receber a entrada de um fluxo ao usar o python 2.6.6, mas com o 3.1.3 eu não. Nota print linenão acordou no 3.1.3, mas print(line)sim.
Ctrl-alt-delor
my python 2.7.5 "for line in sys.stdin", bloqueia até que o EOF ou alguma quantidade razoável de dados seja armazenada em buffer. Ótimo para processamento de fluxo. Não é bom para processamento linha a linha ou entrada do usuário.
Sean
2
Eu suspeito que isso esteja relacionado à detecção de tty na libc, então, quando você o canaliza em um shell interativo, ele não detecta nenhum tty, o buffer do expect-dev é um utilitário útil que acredito injetar um calço via ld_preload, para que is_atty retorne verdadeiro (eu suspeito que é como ele é entregá-lo)
Matt Freeman
8
@ Sean: errado . for line in sys.stdin:não "bloqueia até EOF". Há um bug de leitura antecipada no Python 2 que atrasa as linhas até que o buffer correspondente esteja cheio. É um problema de buffer não relacionado ao EOF. Para solução alternativa, use for line in iter(sys.stdin.readline, ''):(use io.open()para arquivos comuns). Você não precisa dele em Python 3.
jfs
39

Isso fará eco da entrada padrão para a saída padrão:

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

Com base em todas as respostas que sys.stdinvocê usa , você também pode fazer algo como o seguinte para ler um arquivo de argumento, se pelo menos um argumento existir, e voltar ao stdin caso contrário:

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

e use-o como

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

ou

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

ou mesmo

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

Isso faria seu script Python se comportar como muitos programas GNU / Unix, como cat, grepe sed.

Emil Lundberg
fonte
17

argparse é uma solução fácil

Exemplo compatível com as versões 2 e 3 do 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()

Você pode executar este script de várias maneiras:

1. Usando stdin

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

  ou mais curto, substituindo echopor aqui string :

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

2. Usando um argumento de nome de arquivo

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

3. Usando stdinatravés do nome de arquivo especial-

echo 'foo bar' | ./above-script.py -
olibre
fonte
Aqui está uma resposta sobre o que fazer, se o arquivo de entrada estiver compactado: stackoverflow.com/a/33621549/778533 Também é possível fazer add_argument('--in'e, em seguida, canalizar para o script e adicionar --in -à linha de comando. PS innão é um nome muito bom para uma variável / atributo.
Tommy.carstensen
innão é apenas um nome ruim para uma variável, é ilegal. args.in.read()irá gerar o erro InvalidSyntax devido à inpalavra-chave reservada. Pode simplesmente mudar o nome para infilegostar do que os documentos do python argparse fazem: docs.python.org/3/library/…
Ken Colton
Obrigado @ tommy.carstensen pelo seu feedback, eu apenas melhorei a resposta. Feliz Natal e Feliz Ano Novo ;-)
olibre
14

O seguinte chip de código o ajudará (ele lerá todos os stdin bloqueando até EOFem uma sequência):

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

Estou bastante surpreso que ninguém tenha mencionado esse hack até agora:

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

em python2, você pode interromper a set()chamada, mas pode dizer o que quer que seja

Uri Goren
fonte
1
Por readlinesque usar essa divisão em linhas e depois joinnovamente? Você pode simplesmente escreverprint(sys.stdin.read())
musiphil
Isso usará mais memória do que o necessário, porque o python precisa criar uma matriz extra.
Harry Moreno
Bem, na verdade não, porque writeretorna None, e o tamanho do conjunto nunca seria maior que 1 ( =len(set([None])))
Uri Goren
7

Tente o seguinte:

import sys

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

e verifique com:

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

Você pode ler do stdin e, em seguida, armazenar entradas em "dados" da seguinte maneira:

data = ""
for line in sys.stdin:
    data += line
Wei
fonte
O mesmo pode ser feito data = sys.stdin.read(), sem o problema de concatenações de sequência repetidas.
Musiphil
6

Leia a partir sys.stdin, mas para ler dados binários no Windows , você precisa ter cuidado extra, pois sys.stdinele é aberto no modo de texto e ele será corrompido ao ser \r\nsubstituído \n.

A solução é definir o modo como binário se o Windows + Python 2 for detectado e no 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
fonte
4

Eu uso o método a seguir, ele retorna uma string de stdin (eu uso para a análise de json). Funciona com pipe e prompt no Windows (ainda não testado no Linux). Quando solicitado, duas quebras de linha indicam o fim da 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
fonte
3

O problema que tenho com solução

import sys

for line in sys.stdin:
    print(line)

é que, se você não passar nenhum dado para o stdin, ele será bloqueado para sempre. É por isso que adoro esta resposta : verifique se há alguns dados sobre stdin primeiro e depois leia-os. Isto é o que acabei fazendo:

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
fonte
Eu seriamente recomendo esconder essa condição horrível se em um método embora.
Tiktak 4/11
1
Este método limita seriamente a aplicabilidade do programa: por exemplo, você não pode usá-lo para entrada interativa do terminal, porque a entrada quase nunca estará "pronta" quando selectfor chamada; ou você também pode enfrentar problemas se o stdin estiver conectado a um arquivo em uma mídia lenta (rede, CD, fita etc.). Você disse que "se você não passar nenhum dado para o stdin, ele será bloqueado para sempre". é um problema , mas eu diria que é um recurso . A maioria dos programas CLI (por exemplo cat) funciona dessa maneira, e espera-se que eles funcionem. EOF é a única coisa da qual você deve confiar para detectar o final da entrada.
Musiphil 27/02/19
2

Eu tive alguns problemas ao fazer isso funcionar para a leitura de soquetes canalizados para ele. Quando o soquete foi fechado, ele começou a retornar uma string vazia em um loop ativo. Portanto, esta é minha solução (que eu testei apenas no Linux, mas espero que funcione em todos os outros sistemas)

import sys, os
sep=os.linesep

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

Portanto, se você começar a ouvir em um soquete, ele funcionará corretamente (por exemplo, no bash):

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

E você pode chamá-lo com telnet ou apenas apontar um navegador para localhost: 12345

estani
fonte
1

Em relação a este:

for line in sys.stdin:

Eu apenas tentei no python 2.7 (seguindo a sugestão de outra pessoa) para um arquivo muito grande, e não o recomendo, exatamente pelas razões mencionadas acima (nada acontece por um longo tempo).

Acabei com uma solução um pouco mais pitônica (e funciona em arquivos maiores):

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

Então eu posso executar o 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
fonte
Abrir um arquivo não está sendo lido no stdin, como a pergunta. -1
Aaron Hall
Nesse caso, estou passando sys.stdincomo argumento da linha de comando para o script.
Szeitlin
1
Como você pode passar sys.stdincomo argumento da linha de comando para o script? Argumentos são sequências e fluxos são objetos semelhantes a arquivos, não são os mesmos.
DeFazer 17/09/16
Editado pelo @DeFazer para mostrar como usá-lo. Argumentos são cadeias, sim, mas como os docs python e eu mencionado em um comentário anterior acima, sys.stdiné um arquivo-como objeto
szeitlin
1

Para Python 3, isso seria:

# Filename e.g. cat.py
import sys

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

Esta é basicamente uma forma simples de cat (1), pois não adiciona uma nova linha após cada linha. Você pode usar isso (depois de marcar o arquivo executável usando chmod +x cat.py:

echo Hello | ./cat.py
AdamKalisz
fonte
0

Existe o os.read(0, x) que lê xbytes de 0 que representa stdin. Esta é uma leitura sem buffer, com um nível mais baixo que o sys.stdin.read ()

Jay
fonte
0

Ao usar o -ccomando, de uma maneira complicada, em vez de ler o stdin(e mais flexível em alguns casos), você também pode passar um comando de script de shell para o seu comando python colocando o comando de venda entre aspas entre parênteses, iniciado por $sign.

por exemplo

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

Contará o número de linhas do arquivo de histórico do goldendict.

Kasramvd
fonte