Как вы читаете со стандартного ввода?

1474

Я пытаюсь выполнить некоторые задачи по коду в гольф , но все они требуют ввода информации stdin. Как мне получить это в Python?

tehryan
источник

Ответы:

952

Вы можете использовать fileinputмодуль:

import fileinput

for line in fileinput.input():
    pass

fileinput будет проходить по всем строкам ввода, указанным в качестве имен файлов, указанных в аргументах командной строки, или стандартного ввода, если аргументы не предоставлены.

Примечание: lineбудет содержать завершающий перевод строки; чтобы удалить это использоватьline.rstrip()

u0b34a0f6ae
источник
1
@BorislavStoilov И этот ответ правильно отвечает на вопрос: «или стандартный ввод, если аргументы не предоставлены».
Дитмар
1
Документация заявляет, что это отступает от stdin: «Это перебирает строки всех файлов, перечисленных в sys.argv [1:], по умолчанию sys.stdin, если список пуст. Если имя файла равно« - », оно также заменяется by sys.stdin. Чтобы указать альтернативный список имен файлов, передайте его в качестве первого аргумента для input (). Также допускается одно имя файла. "
Арло
721

Есть несколько способов сделать это.

  • sys.stdinэто файл-подобный объект, для которого вы можете вызывать функции, readили readlinesесли вы хотите прочитать все или вы хотите прочитать все и автоматически разделить его на новую строку. (Вам нужно, чтобы import sysэто работало.)

  • Если вы хотите запросить ввод данных у пользователя, вы можете использовать его raw_inputв Python 2.X и только inputв Python 3.

  • Если вы на самом деле просто хотите прочитать параметры командной строки, вы можете получить к ним доступ через список sys.argv .

Вы, вероятно, найдете эту статью Wikibook о вводе / выводе в Python также полезной ссылкой.

Марк Рушаков
источник
446
import sys

for line in sys.stdin:
    print(line)

Обратите внимание, что это будет включать символ новой строки в конце. Чтобы удалить line.rstrip()перевод строки в конце, используйте, как сказал @brittohalloran.

user303110
источник
7
line.rstrip ('\ n'), иначе он
пропустит
Используя этот метод, как мы узнаем, когда заканчивается поток ввода? Я хочу добавить запятую после каждой строки за исключением последней строки.
Зависимым
Я получаю: TypeError: объект FileWrapper не повторяется.
Диего
@avp это не будет правильно иметь дело с \r\nокончаниями строк
josch
228

Python также имеет встроенные функции input()и raw_input(). Смотрите документацию по Python в разделе Встроенные функции .

Например,

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

или

name = input("Enter your name: ")   # Python 3
Пэт Нотц
источник
7
Это читает одну строку, которая на самом деле не то, о чем спрашивал ОП. Я интерпретирую вопрос как «как я могу прочитать несколько строк из дескриптора открытого файла до EOF?»
tripleee
4
ОП не просит читать ввод с клавиатуры, он просит прочитать из стандартного ввода, которое обычно предоставляется участникам конкурса.
Крисфи
это то, что мне было нужно, Google привел меня сюда. Интересно, что мне удалось закодировать теги rfid, datetime, базы данных, но я никогда не удосужился прочитать входные данные от пользователя. lol
clockw0rk
204

Вот из изучения Python :

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

В Unix вы можете проверить это, выполнив что-то вроде:

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

В Windows или DOS вы должны сделать:

C:\> type countlines.py | python countlines.py 
Counted 3 lines.
Евгений Йокота
источник
4
Вот более эффективной памяти (и , возможно , быстрее) способ подсчета строк в Python: print(sum(chunk.count('\n') for chunk in iter(partial(sys.stdin.read, 1 << 15), ''))). смwc-l.py
JFS
11
Использование catздесь является излишним. Правильный вызов для систем Unix является python countlines.py < countlines.py.
Истепанюк
12
«Изучение Python» неправильно в направлении пользователей использовать readlines(). Файловые объекты предназначены для итерации без материализации всех данных в памяти.
Аарон Холл
118

Как вы читаете со стандартного ввода в Python?

Я пытаюсь выполнить некоторые задачи кода, но все они требуют ввода от stdin. Как мне получить это в Python?

Ты можешь использовать:

  • sys.stdin- Файловый объект - вызов, sys.stdin.read()чтобы прочитать все.
  • input(prompt)- передать ему необязательный запрос на вывод, он читает от стандартного ввода до первой новой строки, которую он удаляет. Вам придется делать это несколько раз, чтобы получить больше строк, в конце ввода это вызывает EOFError. (Вероятно, не подходит для игры в гольф.) В Python 2 это так rawinput(prompt).
  • open(0).read()- В Python 3 встроенная функция openпринимает файловые дескрипторы (целые числа, представляющие ресурсы ввода-вывода операционной системы), а 0 - дескриптор stdin. Он возвращает похожий на файл объект sys.stdin- вероятно, ваш лучший выбор для игры в гольф. В Python 2 это так io.open.
  • open('/dev/stdin').read()- похоже open(0), работает на Python 2 и 3, но не на Windows (или даже Cygwin).
  • fileinput.input()- возвращает итератор над строками во всех файлах, перечисленных в sys.argv[1:], или стандартный ввод, если не указан. Используйте как ''.join(fileinput.input()).

И то sysи другое fileinputдолжно быть импортировано, соответственно, конечно.

Быстрые sys.stdinпримеры, совместимые с Python 2 и 3, Windows, Unix

Вам просто нужно readиз sys.stdin, например, если вы данные по конвейеру стандартного ввода:

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

Мы видим, что sys.stdinв текстовом режиме по умолчанию:

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

пример файла

Скажем, у вас есть файл, inputs.txtмы можем принять этот файл и записать его обратно:

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

Более длинный ответ

Вот полная, легко воспроизводимая демонстрационная программа с использованием двух методов: встроенной функции input(используется raw_inputв Python 2) и sys.stdin. Данные не изменены, поэтому обработка не является операцией.

Для начала давайте создадим файл для входных данных:

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

И используя код, который мы уже видели, мы можем проверить, что мы создали файл:

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

Вот справка по sys.stdin.readPython 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в Python 2)

Встроенная функция inputчитает от стандартного ввода до новой строки, которая удаляется (дополняет print, по умолчанию добавляет новую строку.) Это происходит до тех пор, пока не получит EOF (конец файла), после чего она поднимается EOFError.

Таким образом, вот как вы можете использовать inputв Python 3 (или raw_inputв Python 2) чтение из stdin - поэтому мы создаем модуль Python, который мы называем stdindemo.py:

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

И давайте распечатать его обратно, чтобы убедиться, что это так, как мы ожидаем:

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

Опять же, inputчитает до новой строки и по существу удаляет его из строки. printдобавляет новую строку. Поэтому, пока они оба изменяют ввод, их модификации отменяются. (Таким образом, они по существу являются дополнением друг друга.)

И когда inputполучает символ конца файла, он вызывает EOFError, который мы игнорируем и затем выходим из программы.

А в Linux / Unix мы можем передать из cat:

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

Или мы можем просто перенаправить файл из stdin:

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

Мы также можем выполнить модуль как скрипт:

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

Вот справка по встроенному inputиз 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

Здесь мы делаем демонстрационный скрипт, используя sys.stdin. Эффективный способ перебора файловоподобного объекта - использование файловоподобного объекта в качестве итератора. Дополнительный метод для записи в стандартный вывод из этого ввода - просто использовать sys.stdout.write:

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

Распечатайте его обратно, чтобы убедиться, что он выглядит правильно:

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

И перенаправить ввод в файл:

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

Гольф в команду:

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

Файловые дескрипторы для игры в гольф

Поскольку файловые дескрипторы для stdinи stdoutравны 0 и 1 соответственно, мы также можем передать их openв Python 3 (не 2, и обратите внимание, что нам все еще нужна буква 'w' для записи в стандартный вывод).

Если это работает в вашей системе, это сбрит больше символов.

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

Python 2 также io.openделает это, но импорт занимает гораздо больше места:

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

Обращаясь к другим комментариям и ответам

Один комментарий предлагает ''.join(sys.stdin)для игры в гольф, но на самом деле это дольше, чем sys.stdin.read () - плюс Python должен создать дополнительный список в памяти (вот как str.joinработает, когда не дан список) - для контраста:

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

Верхний ответ предполагает:

import fileinput

for line in fileinput.input():
    pass

Но, поскольку sys.stdinреализует файловый API, включая протокол итератора, это то же самое, что и это:

import sys

for line in sys.stdin:
    pass

Другой ответ действительно предлагает это. Просто помните, что если вы делаете это в интерпретаторе, вам нужно Ctrl- dесли вы работаете в Linux или Mac или Ctrl- zв Windows (после Enter), чтобы отправить символ конца файла процессу. Кроме того, этот ответ предлагает print(line)- который добавляет '\n'в конце - использовать print(line, end='')вместо этого (если в Python 2 вам понадобится from __future__ import print_function).

Реальный вариант использования fileinput- для чтения в серии файлов.

Аарон Холл
источник
103

Ответ, предложенный другими:

for line in sys.stdin:
  print line

очень прост и питоничен, но следует отметить, что сценарий будет ждать до EOF, прежде чем начать итерацию по строкам ввода.

Это означает, что tail -f error_log | myscript.pyне будет обрабатывать строки, как ожидалось.

Правильный сценарий для такого варианта использования будет:

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

    if not line:
        break

    print line

ОБНОВЛЕНИЕ
Из комментариев было убрано, что только на python 2 может быть задействована буферизация, так что вы в конечном итоге ожидаете заполнения буфера или EOF перед вызовом печати.

Массимилиано Торромео
источник
8
for line in sys.stdin:Картина не ждать EOF. Но если вы тестируете очень маленькие файлы, ответы могут быть помещены в буфер. Протестируйте с большим количеством данных, чтобы увидеть, что он читает промежуточные результаты.
мб.
Я получаю ожидание окончания файла или буферизации при получении ввода из потока при использовании python 2.6.6, но с 3.1.3 у меня нет. Примечание print lineне проснулся в 3.1.3, но print(line)делает.
Ctrl-Alt-Delor
мой питон 2.7.5 "для строки в sys.stdin", блокирует до EOF или некоторое разумное количество данных буферизовано. Штраф за потоковую обработку. Не подходит для построчной обработки или ввода данных пользователем.
Шон
2
Я подозреваю, что это связано с обнаружением tty в libc, поэтому, когда вы обнаруживаете, что он обнаруживает в интерактивной оболочке, он не обнаруживает tty, небуферизация от ожидаемого-dev - это полезная утилита, которая, как я считаю, вставляет шим через ld_preload, поэтому is_atty возвращает true (я подозреваю, что так оно и есть)
Mâtt Frëëman
8
@Sean: неправильно . for line in sys.stdin:не "блокировать до EOF". В Python 2 есть ошибка опережающего чтения, которая задерживает строки до тех пор, пока соответствующий буфер не будет заполнен. Это проблема буферизации, не связанная с EOF. Чтобы обойти это, используйте for line in iter(sys.stdin.readline, ''):(используйте io.open()для обычных файлов). Вам не нужно это в Python 3.
JFS
39

Это отобразит стандартный ввод в стандартный вывод:

import sys
line = sys.stdin.readline()
while line:
    print line,
    line = sys.stdin.readline()
rlib
источник
31

Основываясь на использовании всех ответов sys.stdin, вы также можете сделать что-то вроде следующего, чтобы прочитать из файла аргументов, если существует хотя бы один аргумент, и в противном случае вернуться к stdin:

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

и использовать его как либо

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

или

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

или даже

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

Это заставит ваш скрипт на Python вести себя как многие программы GNU / Unix, такие как cat, grepи sed.

Эмиль Лундберг
источник
17

argparse это простое решение

Пример совместим с обеими версиями Python 2 и 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()

Вы можете запустить этот скрипт разными способами:

1. Использование stdin

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

  или короче, заменив echoна здесь строки :

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

2. Использование аргумента имени файла

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

3. Использование stdinчерез специальное имя файла-

echo 'foo bar' | ./above-script.py -
olibre
источник
Вот ответ о том, что делать, если входной файл сжат: stackoverflow.com/a/33621549/778533 Можно также сделать, add_argument('--in'а затем направить к сценарию и добавить --in -в командную строку. PS inне очень хорошее имя для переменной / атрибута.
tommy.carstensen
inне просто плохое имя для переменной, это недопустимо. args.in.read()вызовет ошибку InvalidSyntax из-за inзарезервированного ключевого слова. Можно просто переименовать так, infileкак это делают документы на python argparse: docs.python.org/3/library/…
Кен Колтон
Спасибо @ tommy.carstensen за ваш отзыв, я только что улучшил ответ. Веселого Рождества и счастливого Нового года ;-)
olibre
14

Вам поможет следующий фрагмент кода (он будет считывать все блокирующие stdin EOFв одну строку):

import sys
input_str = sys.stdin.read()
print input_str.split()
Чандан Кумар
источник
8

Я очень удивлен, что никто не упомянул этот хак до сих пор:

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

в python2 вы можете отбросить set()вызов, но это будет слово в любом случае

Ури Горен
источник
1
Зачем использовать readlinesэто разделение на строки, а затем joinснова? Вы можете просто написатьprint(sys.stdin.read())
musiphil
Это будет использовать больше памяти, чем необходимо, потому что python должен создать дополнительный массив.
Гарри Морено
Ну, не совсем, потому что writeвозвращается None, и размер набора никогда не будет больше 1 ( =len(set([None])))
Ури Горен
7

Попробуй это:

import sys

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

и проверьте это с:

$ echo "Hello World" | python myFile.py
Boubakr
источник
7

Вы можете читать из stdin и затем сохранять входные данные в «data» следующим образом:

data = ""
for line in sys.stdin:
    data += line
Вэй
источник
То же самое можно сделать data = sys.stdin.read()без проблем повторных конкатенаций строк.
Musiphil
6

Читайте sys.stdin, но чтобы читать двоичные данные в Windows , вам нужно быть очень осторожным, потому sys.stdinчто они открыты в текстовом режиме и будут повреждены, \r\nзаменив их\n .

Решение состоит в том, чтобы установить режим в двоичный режим, если обнаружен Windows + Python 2, и использовать Python 3 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

Я использую следующий метод, он возвращает строку из стандартного ввода (я использую его для анализа JSON). Он работает с pipe и подсказкой в ​​Windows (пока не тестировался в Linux). При запросе два разрыва строки указывают на конец ввода.

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
источник
3

Проблема у меня с решением

import sys

for line in sys.stdin:
    print(line)

в том, что если вы не передадите данные в stdin, он будет заблокирован навсегда. Вот почему мне нравится этот ответ : сначала проверьте, есть ли какие-то данные на stdin, а затем прочитайте их. Это то, что я в итоге сделал:

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)
Томас Томечек
источник
Я бы серьезно рекомендовал скрыть это отвратительное условие в методе.
Тиктак
1
Этот метод серьезно ограничивает применимость программы: например, вы не можете использовать это для интерактивного ввода с терминала, потому что при selectвызове ввод почти никогда не будет «готов» ; или вы также можете столкнуться с проблемами, если stdin подключен к файлу на медленном носителе (сеть, компакт-диск, лента и т. д.). Вы сказали, что «если вы не передадите данные в stdin, они будут заблокированы навсегда». это проблема , но я бы сказал, что это особенность . Большинство программ CLI (например cat) работают таким образом, и они ожидаются. EOF - единственная вещь, от которой вы должны зависеть, чтобы определить конец ввода.
Musiphil
2

У меня были некоторые проблемы при получении этого для работы через сокеты, переданные по каналу. Когда сокет закрылся, он начал возвращать пустую строку в активном цикле. Так что это мое решение (которое я тестировал только в Linux, но надеюсь, что оно работает во всех других системах)

import sys, os
sep=os.linesep

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

Поэтому, если вы начнете прослушивать сокет, он будет работать правильно (например, в bash):

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

И вы можете позвонить с помощью telnet или просто указать браузер на localhost: 12345

estani
источник
1

В соответствии с этим:

for line in sys.stdin:

Я только что попробовал это на python 2.7 (следуя чьему-либо предложению) для очень большого файла, и я не рекомендую его именно по причинам, указанным выше (в течение долгого времени ничего не происходит).

Я получил немного более питонное решение (и оно работает с большими файлами):

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

Затем я могу запустить скрипт локально как:

python myscript.py "0 1 2 3 4..." # can be a multi-line string or filename - any std.in input will work
szeitlin
источник
Открытие файла не читает со стандартного ввода, как вопрос. -1
Аарон Холл
В этом случае я передаю в sys.stdinкачестве аргумента командной строки сценарий.
szeitlin
1
Как вы могли бы передать sys.stdinв качестве аргумента командной строки сценарий? Аргументы - это строки, а потоки - это файловые объекты, они не одинаковы.
DeFazer
@DeFazer отредактирован, чтобы показать, как его использовать. Аргументы - это строки, да, но, как я уже упоминал в документации к Python в предыдущем комментарии, sys.stdinэто
файлоподобный
1

Для Python 3 это будет:

# Filename e.g. cat.py
import sys

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

Это в основном простая форма cat (1), так как она не добавляет символ новой строки после каждой строки. Вы можете использовать это (после того, как Вы отметили исполняемый файл, используя chmod +x cat.pyтакие как:

echo Hello | ./cat.py
AdamKalisz
источник
0

Существует, os.read(0, x) который читает xbytes от 0, который представляет стандартный ввод. Это небуферизованное чтение, более низкий уровень, чем sys.stdin.read ()

сойка
источник
0

При использовании -cкоманды, как хитрый способ, вместо чтения stdin(и в некоторых случаях более гибкого) вы можете передать команду сценария оболочки также вашей команде python, поместив команду sell в кавычки в скобках, начинающихся с$ знака.

например

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

Это будет считать количество строк из файла истории Goldendict.

Kasramvd
источник