Як ти читаєш зі stdin?


Відповіді:


950

Ви можете використовувати fileinputмодуль:

import fileinput

for line in fileinput.input():
    pass

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

Примітка: lineміститиме зворотний новий рядок; щоб видалити його, використовуйтеline.rstrip()


1
@BorislavStoilov І ця відповідь правильно відповідає на питання: "або стандартне введення, якщо аргументи не наводяться".
Дітмар

1
Документація стверджує, що вона повертається до stdin: "Це повторює рядки всіх файлів, перелічених у sys.argv [1:], за замовчуванням до sys.stdin, якщо список порожній. Якщо ім'я файлу" - ", його також замінюють Для вказівки альтернативного списку імен файлів, передайте його як перший аргумент для введення (). Також дозволено одне ім’я файлу. "
Арло

721

Є кілька способів це зробити.

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

  • Якщо ви хочете запропонувати користувачеві ввести, ви можете використовувати raw_inputв Python 2.X і просто inputв Python 3.

  • Якщо ви дійсно хочете прочитати параметри командного рядка, ви можете отримати доступ до них через список sys.argv .

Напевно, ця стаття Wikibook про введення-виведення в Python також стане корисною довідкою.


445
import sys

for line in sys.stdin:
    print(line)

Зауважте, що це буде містити символ нового рядка в кінці. Щоб видалити line.rstrip()нову лінію в кінці, використовуйте як сказано @brittohalloran.


7
line.rstrip ('\ n'), інакше вона скине всі пробіли
avp

використовуючи цей метод, як ми можемо знати, коли закінчується вхідний потік? Я хочу додати кому після кожного вилучення рядків для останнього рядка.
залежний

Я отримую: 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?"
трійка

4
ОП не просить прочитати вхід з клавіатури, він просить прочитати з stdin, який в умовах змагань зазвичай надається учасникам.
хриси

це те, що мені було потрібно, google привів мене сюди. Цікаво, що мені вдалося
кодувати rfid

204

Ось з Learning 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

Як ви читаєте зі stdin в Python?

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

Ви можете використовувати:

  • sys.stdin- Файлоподібний об’єкт - дзвінок, sys.stdin.read()щоб прочитати все.
  • input(prompt)- передайте йому необов'язковий запит на вихід, він читає від stdin до першого нового рядка, який він викреслює. Вам доведеться робити це неодноразово, щоб отримати більше рядків, наприкінці введення він піднімає EOFError. (Мабуть, це не чудово для гри в гольф.) У Python 2 це так rawinput(prompt).
  • open(0).read()- У Python 3 вбудована функція openприймає дескриптори файлів (цілі числа, що представляють ресурси IO операційної системи), а 0 - дескриптор stdin. Він повертає файлоподібний об'єкт на кшталт sys.stdin- мабуть, найкраща ставка для гольфу. У Python 2 це так io.open.
  • open('/dev/stdin').read()- аналогічно open(0), працює на Python 2 і 3, але не в Windows (або навіть Cygwin).
  • fileinput.input()- повертає ітератор по рядках у всіх файлах, перелічених у sys.argv[1:], або stdin, якщо вони не вказані. Використовуйте як ''.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.readз 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в Python 2)

Вбудована функція inputчитає від стандартного вводу до нового рядка, який знімається (доповнюючи print, який додає новий рядок за замовчуванням.) Це відбувається до тих пір, поки не отримає EOF (End Of File), після чого він піднімається 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. Ефективним способом ітерації над файлоподібним об'єктом є використання файлоподібного об'єкта в якості ітератора. Додатковим методом запису в stdout з цього вводу є просто використання 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' для запису до stdout).

Якщо це працює у вашій системі, воно збрине більше символів.

$ 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. Але якщо ви протестуєте на дуже маленьких файлах, відповіді можуть бути буферизовані. Тестуйте з більшою кількістю даних, щоб побачити, чи читає він проміжні результати.
mb.

Я зачекаю Кінець файлу або буферизацію, коли беруть вхід з потоку при використанні python 2.6.6, але з 3.1.3 я цього не роблю. Примітка print lineне прокидається в 3.1.3, але так print(line)і є.
ctrl-alt-delor

мій python 2.7.5 "для рядка в sys.stdin", блокує до EOF або якийсь розумний об'єм даних. Штраф для обробки потоку. Недоречно для оброблення рядка за рядком або введення користувачем.
Шон

2
Я підозрюю, що це пов'язано з виявленням tty в libc, тому, коли ви передаєте його, він виявляє інтерактивну оболонку, він не виявляє жодних tty, unbuffer from очаква-dev - це зручна утиліта, на яку я вважаю, що вводить shim через ld_preload, тому is_atty повертає справжнє (я підозрюєте, що так воно передає)
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()

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 -

Ось відповідь, що робити, якщо стиснутий вхідний файл: stackoverflow.com/a/33621549/778533 Можна також зробити, add_argument('--in'а потім передати в сценарій та додати --in -до командного рядка. PS in- це не дуже добре ім'я для змінної / атрибута.
tommy.carstensen

inце не просто неправильне ім’я змінної, це незаконне. args.in.read()призведе до помилки InvalidSyntax через inзарезервоване ключове слово. Можна просто перейменувати, щоб infileподобатися doc-аргументації
Кен Колтон,

Дякую @ tommy.carstensen за Ваш відгук, я лише вдосконалив відповідь. З Різдвом та новим роком ;-)
олибре

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

7

Ви можете читати з stdin, а потім зберігати вхідні дані у "дані" таким чином:

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

Я використовую наступний метод, він повертає рядок з stdin (я використовую його для розбору json). Він працює з pipe та prompt в 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

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()

Тож якщо ви почнете слухати сокет, він буде працювати належним чином (наприклад, в баші):

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

І ви можете зателефонувати за допомогою telnet або просто навести браузер на localhost: 12345


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

Відкриття файлу не читається зі stdin, як запитання. -1
Аарон Холл

У цьому випадку я передаю sys.stdinскрипт як аргумент командного рядка.
szeitlin

1
Як ви могли передати sys.stdinскрипт як аргумент командного рядка? Аргументи - це рядки, а потоки - файлоподібні об'єкти, вони не однакові.
DeFazer

@DeFazer відредагований, щоб показати, як ним користуватися. Аргументи - це рядки, так, але, як документи python, і я вже згадував у попередньому коментарі вище, sys.stdinє об'єктом, подібним до файлів
szeitlin

1

Для Python 3 це було б:

# Filename e.g. cat.py
import sys

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

Це в основному проста форма кота (1), оскільки вона не додає новий рядок після кожного рядка. Ви можете використовувати це (після того як ви позначили файл, який виконується, використовуючи chmod +x cat.pyтакі:

echo Hello | ./cat.py

0

Там, os.read(0, x) який читає xbytes з 0, що представляє stdin. Це нерозподілений зчитування, більш низький рівень, ніж sys.stdin.read ()


0

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

напр

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

Це підрахує кількість рядків з файлу історії goldendict.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.