你如何从标准输入读取?
Posted
技术标签:
【中文标题】你如何从标准输入读取?【英文标题】:How do you read from stdin? 【发布时间】:2010-11-29 20:10:12 【问题描述】:我正在尝试做一些code golf 挑战,但它们都需要从stdin
获取输入。我如何在 Python 中获得它?
【问题讨论】:
【参考方案1】:来自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.
【讨论】:
这是一种在 Python 中计算行数的内存效率更高(也许更快)的方法:print(sum(chunk.count('\n') for chunk in iter(partial(sys.stdin.read, 1 << 15), '')))
。 see wc-l.py
这里使用cat
是多余的。 Unix 系统的正确调用是python countlines.py < countlines.py
。
"Learning Python" 指导用户使用readlines()
是错误的。文件对象旨在进行迭代,而无需具体化内存中的所有数据。
@istepaniuk 关于“使用猫”,我发现在我调整过滤器的命令行参数时使用cat filespec | filters
通常更方便,因为它们每次都会在行尾.
@GeePokey 输入重定向也可以放在前面:< filespec filters
【参考方案2】:
有几种方法可以做到这一点。
sys.stdin
是一个类似文件的对象,如果您想读取所有内容,或者您想读取所有内容并自动用换行符分割,您可以在其上调用函数read
或readlines
。 (您需要 import sys
才能使用。)
如果你想提示用户输入,你可以在 Python 2.X 中使用 raw_input
,在 Python 3 中只使用 input
。
如果您实际上只是想阅读命令行选项,可以通过sys.argv 列表访问它们。
您可能会发现this Wikibook article on I/O in Python 也是一个有用的参考。
【讨论】:
第三个选项是我在 code.golf 处理输入所要寻找的【参考方案3】:您可以使用fileinput
模块:
import fileinput
for line in fileinput.input():
pass
fileinput
将遍历输入中指定为命令行参数中给出的文件名的所有行,如果没有提供参数,则循环访问标准输入。
注意:line
将包含一个尾随换行符;删除它使用line.rstrip()
【讨论】:
input()
和 fileinput.input()
有什么区别?
@AmirrezaRiahi input()
从标准输入读取一行,而fileinput.input()
将循环遍历所有输入中指定为命令行参数中给出的文件名的行, 或标准输入(如果没有提供参数)
感谢您提出这个问题。当我只想读取标准输入时,其中一条 fileinput.input() 行是我的命令行 arg 时,我被卡住了。【参考方案4】:
import sys
for line in sys.stdin:
print(line)
请注意,这将在末尾包含一个换行符。要删除末尾的换行符,请使用 @brittohalloran 所说的 line.rstrip()
。
【讨论】:
line.rstrip('\n'),否则会删除所有空格 使用这个方法,我们怎么知道输入流什么时候结束呢?我想在最后一行的每一行 excepts 之后添加逗号。 我收到:TypeError: 'FileWrapper' 对象不可迭代。 @avp 这将无法正确处理\r\n
行尾
在末尾删除多余换行符的另一种方法是使用print(line, end='')
。【参考方案5】:
Python 还具有内置函数 input()
和 raw_input()
。请参阅Built-in Functions 下的 Python 文档。
例如,
name = raw_input("Enter your name: ") # Python 2.x
或
name = input("Enter your name: ") # Python 3
【讨论】:
这读取一行,这并不是 OP 真正询问的内容。我将问题解释为“如何从打开的文件句柄中读取一堆行直到 EOF?” OP 不是要求从键盘读取输入,而是要求从标准输入中读取,在比赛情况下通常会提供给参赛者。 这是我需要的,谷歌把我带到了这里。有趣的是,我设法编写了 rfid 标签、日期时间、数据库,但从不费心去读取用户的输入 lol【参考方案6】:别人提出的答案:
for line in sys.stdin:
print line
非常简单和pythonic,但必须注意,脚本将等到EOF,然后开始迭代输入的行。
这意味着tail -f error_log | myscript.py
不会按预期处理行。
这种用例的正确脚本是:
while 1:
try:
line = sys.stdin.readline()
except KeyboardInterrupt:
break
if not line:
break
print line
更新 从 cmets 中可以看出,仅在 python 2 上可能会涉及缓冲,因此在发出 print 调用之前,您最终会等待缓冲区填充或 EOF。
【讨论】:
for line in sys.stdin:
模式不等待 EOF。但是,如果您在非常小的文件上进行测试,响应可能会被缓冲。用更多数据进行测试,看看它是否读取中间结果。
在使用 python 2.6.6 从流中获取输入时,我会等待文件结束或缓冲,但在 3.1.3 中我没有。注意 print line
不会在 3.1.3 中唤醒,但 print(line)
会。
我的 python 2.7.5 "for line in sys.stdin",阻塞直到 EOF 或一些合理数量的数据被缓冲。适合流处理。不适用于逐行处理或用户输入。
我怀疑这与在 libc 中检测到 tty 有关,因此当您在交互式 shell 上通过管道检测到它时,它检测不到 tty,来自 expect-dev 的 unbuffer 是一个方便的工具,我相信它会注入一个 shim通过 ld_preload 所以 is_atty 返回 true (我怀疑这就是它的处理方式)
@Sean:错误。 for line in sys.stdin:
不会“阻塞到 EOF”。有一个read-ahead bug in Python 2 会延迟行,直到相应的缓冲区已满。这是一个与 EOF 无关的缓冲问题。要解决此问题,请使用for line in iter(sys.stdin.readline, ''):
(对于普通文件使用io.open()
)。在 Python 3 中你不需要它。【参考方案7】:
这会将标准输入回显到标准输出:
import sys
line = sys.stdin.readline()
while line:
print line,
line = sys.stdin.readline()
【讨论】:
【参考方案8】:在所有使用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 程序,例如 cat
、grep
和 sed
。
【讨论】:
【参考方案9】:试试这个:
import sys
print sys.stdin.read().upper()
并检查:
$ echo "Hello World" | python myFile.py
【讨论】:
【参考方案10】:您可以从标准输入读取,然后将输入存储到 "data" 中,如下所示:
data = ""
for line in sys.stdin:
data += line
【讨论】:
"do not rely on CPython's efficient implementation of in-place string concatenation for statements in the form a += b or a = a + b . This optimization is fragile even in CPython"data = sys.stdin.read()
也可以做同样的事情,不会出现字符串重复拼接的问题。【参考方案11】:
在通过管道读取它的套接字时,我遇到了一些问题。当套接字关闭时,它开始在活动循环中返回空字符串。所以这是我的解决方案(我只在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
【讨论】:
【参考方案12】:下面的代码可以帮助你(它将所有标准输入阻塞到EOF
,读入一个字符串):
import sys
input_str = sys.stdin.read()
print input_str.split()
【讨论】:
【参考方案13】:关于这个:
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
【讨论】:
打开文件不是从标准输入读取,就像问题所问的那样。 -1 在这种情况下,我将sys.stdin
作为命令行参数传递给脚本。
如何将sys.stdin
作为命令行参数传递给脚本?参数是字符串,流是类文件对象,它们是不一样的。
@DeFazer 编辑以展示如何使用它。参数是字符串,是的,但是正如我在前面的评论中提到的 python 文档,sys.stdin
是一个类似文件的对象【参考方案14】:
如何在 Python 中读取标准输入?
我正在尝试做一些代码高尔夫挑战,但它们都需要从标准输入中获取输入。我如何在 Python 中获得它?
你可以使用:
sys.stdin
- 类似文件的对象 - 调用 sys.stdin.read()
读取所有内容。
input(prompt)
- 将可选提示传递给输出,它从标准输入读取到第一个换行符,然后将其剥离。您必须反复执行此操作才能获得更多行,在输入结束时它会引发 EOFError。 (可能不适合打高尔夫球。)在 Python 2 中,这是rawinput(prompt)
。
open(0).read()
- 在 Python 3 中,内置函数 open
接受 file descriptors(代表操作系统 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:]
中列出的所有文件中的行上返回一个迭代器,如果没有给出标准输入。像''.join(fileinput.input())
一样使用。
sys
和 fileinput
当然必须分别导入。
Quick sys.stdin
示例兼容 Python 2 和 3、Windows、Unix
您只需要从sys.stdin
到read
,例如,如果您将数据通过管道传输到标准输入:
$ 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
(在 Python 2 中使用 raw_input
)和 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
以下是 Python 3 对 sys.stdin.read
的帮助:
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
(Python 2 中为raw_input
)
内置函数input
从标准输入读取到换行符,该换行符被剥离(补充print
,默认情况下添加换行符。)这种情况会一直发生,直到它获得EOF(文件结束),此时它引发了EOFError
。
因此,以下是如何使用 Python 3 中的 input
(或 Python 2 中的 raw_input
)从标准输入读取的方法 - 因此我们创建了一个称为 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
或者我们可以直接从标准输入重定向文件:
$ python -m stdindemo < inputs.txt
foo
bar
baz
我们也可以将模块作为脚本执行:
$ python stdindemo.py < inputs.txt
foo
bar
baz
这是 Python 3 中内置的 input
的帮助:
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,我们也可以在 Python 3 中将它们传递给 open
(不是 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
另一个答案确实表明了这一点。请记住,如果您在解释器中执行此操作,如果您在 Linux 或 Mac 上,则需要执行 Ctrl-d 或 Ctrl Windows 上的 kbd>-z(在 Enter 之后)将文件结束符发送到进程。此外,该答案建议print(line)
- 它在末尾添加'\n'
- 改用print(line, end='')
(如果在Python 2中,您将需要from __future__ import print_function
)。
fileinput
的真正用例是读取一系列文件。
【讨论】:
【参考方案15】:从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()
【讨论】:
【参考方案16】:我很惊讶到目前为止没有人提到这个黑客:
python -c "import sys; set(map(sys.stdout.write,sys.stdin))"
在 python2 中,您可以放弃 set()
调用,但无论哪种方式都可以
【讨论】:
为什么要使用readlines
分割成行,然后再使用join
?你可以写print(sys.stdin.read())
这将使用比需要更多的内存,因为 python 需要构建一个额外的数组。
嗯,不是真的,因为write
返回None
,并且集合大小永远不会大于1 (=len(set([None]))
)【参考方案17】:
我遇到的问题
import sys
for line in sys.stdin:
print(line)
是如果你不向标准输入传递任何数据,它将永远阻塞。这就是我喜欢this answer的原因:先检查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)
【讨论】:
我强烈建议将这个可怕的 if 条件隐藏到方法中。 此方法严重限制了程序的适用性:例如,您不能将其用于终端的交互式输入,因为在调用select
时输入几乎永远不会“准备好”;或者,如果标准输入连接到慢速介质(网络、CD、磁带等)上的文件,您也可能会遇到问题。您说“如果您不将任何数据传递给标准输入,它将永远阻塞。”是一个问题,但我会说这是一个功能。大多数 CLI 程序(例如 cat
)都以这种方式工作,而且它们也应该如此。 EOF 是您检测输入结束时唯一应该依赖的东西。【参考方案18】:
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
替换为here string:
./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 -
【讨论】:
如果输入文件被压缩,这是一个关于做什么的答案:***.com/a/33621549/778533 也可以执行add_argument('--in'
,然后通过管道传输到脚本并将--in -
添加到命令行。附言in
不是一个很好的变量/属性名称。
in
不仅是变量的坏名,而且是非法的。由于in
保留关键字,args.in.read()
将引发 InvalidSyntax 错误。可以像 python argparse 文档那样简单地重命名为infile
:docs.python.org/3/library/…
感谢@tommy.carstensen 的反馈,我刚刚改进了答案。圣诞快乐,新年快乐 ;-)【参考方案19】:
有
os.read(0, x)
它从代表标准输入的 0 读取 xbytes。这是一个无缓冲的读取,比 sys.stdin.read() 级别更低
【讨论】:
【参考方案20】:对于 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
【讨论】:
【参考方案21】:当使用-c
命令时,作为一种棘手的方式,您可以通过将shell 命令放在引号中,而不是读取stdin
(在某些情况下更灵活),也可以将shell 脚本命令传递给您的python 命令在以$
符号开头的括号内。
例如
python3 -c "import sys; print(len(sys.argv[1].split('\n')))" "$(cat ~/.goldendict/history)"
这将计算 Goldendict 历史文件中的行数。
【讨论】:
这很聪明,我以这种方式输入python -c
,这是一个有趣的解决方法。谢谢你的分享。 :)【参考方案22】:
我使用以下方法,它从标准输入返回一个字符串(我用它来解析 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
【讨论】:
【参考方案23】:从 Python 3.8 开始你可以使用assignment expression:
while (line := input()):
print(line)
【讨论】:
以上是关于你如何从标准输入读取?的主要内容,如果未能解决你的问题,请参考以下文章