您如何从stdin阅读?


1470

我正在尝试挑战一些代码挑战,但是所有这些挑战都需要从输入stdin。我如何在Python中得到它?

Answers:


949

您可以使用以下fileinput模块:

import fileinput

for line in fileinput.input():
    pass

fileinput 将在输入中指定为命令行参数中指定的文件名的所有行,如果没有提供参数则返回标准输入。

注意:line将包含尾随换行符;删除它使用line.rstrip()


1
@BorislavStoilov这个答案确实正确回答了这个问题:“或者如果没有提供参数,则是标准输入”。
Dietmar

1
文档指出它回退到stdin:“这会遍历sys.argv [1:]中列出的所有文件的行,如果列表为空,则默认为sys.stdin。如果文件名是'-',则也将替换它要指定文件名的替代列表,请将其作为输入参数的第一个参数传递给input()。也允许使用单个文件名。”
阿罗

721

有几种方法可以做到。

  • sys.stdin是一个类似于文件的对象,可以在其上调用函数,read或者readlines如果要读取所有内容,或者要读取所有内容并自动按换行符将其拆分,则可以在其上调用。(您需要使它import sys起作用。)

  • 如果要提示用户输入,则可以raw_input在Python 2.X和inputPython 3中使用。

  • 如果您实际上只想阅读命令行选项,则可以通过sys.argv列表访问它们。

您可能还会发现有关I / O和Python的Wikibook这篇文章也是有用的参考。


445
import sys

for line in sys.stdin:
    print(line)

请注意,这将在末尾包含换行符。要最后删除换行符,请使用line.rstrip()@brittohalloran所说的。


7
line.rstrip('\ n'),否则它将删除所有空格
avp

使用这种方法,我们如何知道输入流何时结束?除了最后一行,我想在每行之后添加逗号。
沉迷

我收到:TypeError:“ FileWrapper”对象不可迭代。
Diego Queiroz

@avp,这将无法正确处理\r\n行尾
josch 19'Jul

228

Python还具有内置函数input()raw_input()。请参阅“ 内置函数”下的Python文档。

例如,

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

要么

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

7
这读一行,这并不是OP真正要求的。我将问题解释为“如何从一个打开的文件句柄读取一行代码,直到EOF?”
2015年

4
OP并不要求读取键盘输入,而是要求从stdin读取内容,在比赛情况下,stdin通常提供给参赛者。
基督

这就是我所需要的,google将我带到了这里。有趣的是,我设法对rfid标签,日期时间,数据库进行了编码,但从来没有理会读取用户的输入,哈哈
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 2012年

11
cat这里的使用是多余的。对于Unix系统,正确的调用是python countlines.py < countlines.py
istepaniuk

12
引导用户使用“学习Python”是错误的readlines()。文件对象旨在在不具体化内存中所有数据的情况下进行迭代。
亚伦·霍尔

118

您如何从Python的stdin中读取信息?

我正在尝试进行一些代码挑战,但是它们都要求输入来自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())

双方sysfileinput必须进口的,分别的,当然。

sys.stdin与Python 2和3,Windows,Unix兼容的快速示例

例如,如果您将数据通过管道传输到stdin,则只需要readfrom 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

更长的答案

这是一个完整的,易于复制的演示,使用两种方法,内建函数inputraw_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.

内置函数inputraw_input在Python 2中)

内置函数input从标准输入读取到换行符,然后将其剥离(补码print,默认情况下会添加换行符)。此过程一直持续到得到EOF(文件结束)为止,此时它引发EOFError

因此,这是input在Python 3(或raw_inputPython 2)中用于从stdin读取的方法-因此我们创建了一个称为stdindemo.py的Python模块:

$ 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

这是inputPython 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

高尔夫的文件描述符

由于文件描述符stdinstdout为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)-最终会增加a-的'\n'使用print(line, end='')(如果在Python 2中,则需要from __future__ import print_function)。

真正的用例fileinput是读取一系列文件。


103

其他人提出的答案:

for line in sys.stdin:
  print line

是非常简单且具有Python风格的代码,但必须注意,脚本将等到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 2012年

我的python 2.7.5“用于sys.stdin中的行”,一直阻塞到EOF或一些合理数量的数据被缓冲为止。适用于流处理。不适用于逐行处理或用户输入。
肖恩

2
我怀疑这与libc中tty的检测有关,因此当您通过管道在交互式shell上检测到tty时,它不会检测到tty,从Expect-dev进行的缓冲是一个方便的工具,我相信它会通过ld_preload注入垫片,因此is_atty返回true(I怀疑这是如何处理的))
MâttFrëëman15年

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,则还可以在所有变量上进行构建,如果至少存在一个自变量,则还可以执行以下操作从一个自变量文件中读取,否则返回标准输入:

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程序,例如catgrepsed


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()由于in保留关键字,将引发InvalidSyntax错误。可以简单地重命名为infile像蟒蛇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需要建立一个额外的数组。
哈里·莫雷诺

好吧,不是真的,因为writereturn None和设置的大小永远不会大于1(=len(set([None]))
Uri Goren

7

尝试这个:

import sys

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

并使用以下命令进行检查:

$ echo "Hello World" | python myFile.py


6

Windows读取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解析)。它适用于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)

我会严重建议将这种丑陋的条件隐藏到一个方法中。
tiktak

1
这种方法严重限制了程序的适用性:例如,您不能将其用于来自终端的交互式输入,因为select调用时输入几乎永远不会“准备就绪” 。如果stdin连接到慢速介质(网络,CD,磁带等)上的文件,也可能会遇到问题。您说过:“如果不将任何数据传递给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


1

关于此:

for line in sys.stdin:

我只是在python 2.7上尝试了一个很大的文件(遵循别人的建议),但出于上述原因(长时间未发生任何事情),我不建议这样做。

我最终得到了一个稍微多一点的pythonic解决方案(它适用于更大的文件):

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'8

1
您如何sys.stdin作为命令行参数传递给脚本?参数是字符串,流是类似文件的对象,它们是不同的。
DeFazer

@DeFazer编辑以显示如何使用它。参数是字符串,是的,但是正如我在上面的早期注释中提到的python docs一样,它sys.stdin是一个类似文件的对象
szeitlin

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

0

os.read(0, x) 从0表示标准输入读取xbytes。这是一个无缓冲的读取,比sys.stdin.read()的级别低。


0

使用-c命令时,以一种棘手的方式,您无需阅读stdin(在某些情况下更为灵活),就可以通过将sell命令放在以引号开头的括号内的引号中来将Shell脚本命令传递给python命令$

例如

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

这将计算goldendict的历史记录文件中的行数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.