ChatGPT解决这个技术问题 Extra ChatGPT

如何从标准输入读取?

如何从 stdin 读取?一些 code golf 挑战需要使用 stdin 输入。


9
9 revs, 9 users 45%

使用 fileinput 模块:

import fileinput

for line in fileinput.input():
    pass

fileinput 将遍历输入中指定为命令行参数中给出的文件名的所有行,如果没有提供参数,则循环访问标准输入。

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


input()fileinput.input() 有什么区别?
@AmirrezaRiahi input() 从标准输入读取一行,而 fileinput.input() 将遍历 all 输入中指定为命令行参数中给出的文件名的行,如果没有参数,则为标准输入提供
谢谢你问这个。当我只想读取标准输入时,当 fileinput.input() 行之一是我的命令行 arg 时,我被卡住了。
为什么不使用输入?否则你在一个 for 循环中,而我只想要一个输入。
如果您希望将这些行作为列表,您可以使用 list(fileinput.input())
d
dynamicwebpaige

有几种方法可以做到这一点。

sys.stdin 是一个类似文件的对象,如果您想读取所有内容,或者您想读取所有内容并自动用换行符分割,您可以在其上调用函数 read 或 readlines 。 (您需要导入 sys 才能正常工作。)

如果要提示用户输入,可以在 Python 2.X 中使用 raw_input,在 Python 3 中只需输入即可。

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

您可能会发现 this Wikibook article on I/O in Python 也是有用的参考。


第三个选项是我在 code.golf 处理输入的方法
p
phoenix
import sys

for line in sys.stdin:
    print(line)

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


line.rstrip('\n'),否则将删除所有空格
使用这种方法,我们如何知道输入流何时结束?我想在除最后一行之外的每一行之后添加逗号。
我收到:TypeError:'FileWrapper' 对象不可迭代。
@avp 这将无法正确处理 \r\n 行结尾
在末尾删除额外换行符的另一种方法是使用 print(line, end='')
Z
Zero Piraeus

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 标签、日期时间、数据库,但从不费心去读取用户的输入,哈哈
4
4 revs, 4 users 80%

以下来自 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
“学习 Python”指导用户使用 readlines() 是错误的。文件对象旨在被迭代而不实现内存中的所有数据。
@istepaniuk 关于“猫的使用”,我发现在调整过滤器的命令行参数时,通常使用 cat filespec | filters 更方便,因为它们每次都位于行尾。
@GeePokey 输入重定向也可以放在前面:< filespec filters
C
Community

你如何在 Python 中读取标准输入?我正在尝试做一些代码高尔夫挑战,但它们都需要从标准输入中获取输入。我如何在 Python 中得到它?

您可以使用:

sys.stdin - 一个类似文件的对象 - 调用 sys.stdin.read() 来读取所有内容。

input(prompt) - 将可选提示传递给输出,它从 stdin 读取到第一个换行符,然后将其剥离。您必须反复执行此操作才能获得更多行,在输入结束时它会引发 EOFError。 (可能不适合打高尔夫球。)在 Python 2 中,这是 rawinput(prompt)。

open(0).read() - 在 Python 3 中,内置函数 open 接受文件描述符(表示操作系统 IO 资源的整数),0 是标准输入的描述符。它返回一个类似文件的对象,如 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

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

例如,如果您将数据通过管道传输到标准输入,您只需要从 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.

内置函数,输入(Python 2 中的 raw_input)

内置函数 input 从标准输入读取到换行符,该换行符被剥离(补充 print,默认情况下添加换行符。)这种情况发生直到它获得 EOF(文件结束),此时它引发 { 3}。

因此,以下是如何使用 Python 3 中的 input(或 Python 2 中的 raw_input)从 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

或者我们可以从标准输入重定向文件:

$ 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.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,我们也可以在 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-dCtrl-z(在 Enter 之后)将文件结束符发送到进程。此外,该答案建议 print(line) - 它在末尾添加 '\n' - 使用 print(line, end='') 代替(如果在 Python 2 中,您将需要 from __future__ import print_function)。

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


M
Massimiliano Torromeo

其他人提出的答案:

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

更新从评论中可以看出,仅在 python 2 上可能涉及缓冲,因此在发出打印调用之前,您最终会等待缓冲区填充或 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 是一个方便的工具,我相信它通过 ld_preload 注入 shim 所以 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 中不需要它。
S
S. Liu

这会将标准输入回显到标准输出:

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

W
WestCoastProjects

在使用 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


o
oHo

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 -

如果输入文件被压缩,以下是关于如何执行的答案:stackoverflow.com/a/33621549/778533 也可以执行 add_argument('--in',然后通过管道传输到脚本并将 --in - 添加到命令行。 PS in 不是一个很好的变量/属性名称。
in 不仅是变量的坏名,而且是非法的。由于 in 保留关键字,args.in.read() 将引发 InvalidSyntax 错误。可以像 python argparse 文档那样简单地重命名为 infiledocs.python.org/3/library/…
谢谢@tommy.carstensen 的反馈,我刚刚改进了答案。圣诞快乐和新年快乐 ;-)
r
rogerdpack

以下代码片将为您提供帮助(它将所有 stdin 阻塞读取到 EOF 到一个字符串中):

import sys
input_str = sys.stdin.read()
print input_str.split()

A
Alexis Wilke

我很惊讶到目前为止没有人提到这个黑客:

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])))
B
Bouba

尝试这个:

import sys

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

并检查:

$ echo "Hello World" | python myFile.py

T
Tanveer Alam

您可以从标准输入读取,然后将输入存储到“数据”中,如下所示:

data = ""
for line in sys.stdin:
    data += line

a
anatoly techtonik

sys.stdin 读取,但要在 Windows 上读取二进制数据,您需要格外小心,因为 sys.stdin 以文本模式打开,它会损坏 \r\n 将它们替换为 {4 }。

解决方案是在检测到 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()

B
Bouni

我使用以下方法,它从标准输入返回一个字符串(我用它来解析 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

A
AdamKalisz

对于 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

T
Tomas Tomecek

我有解决方案的问题

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 是您检测输入结束时唯一应该依赖的东西。
h
hant0508

从 Python 3.8 开始,您可以使用 assignment expression

while (line := input()):
    print(line)

当标准输入结束时,这不会产生 EOFError 吗?我无法通过使用 input() 来解决这个问题。
在 Python 3.8 上,它确实会产生 EOFError: EOF when reading a line。要绕过您可以使用 try ... except EOFError: pass
M
Mazdak

当使用 -c 命令时,作为一种棘手的方式,您可以通过将 shell 命令放在括号内的引号中,而不是读取 stdin(在某些情况下更灵活),也可以将 shell 脚本命令传递给您的 python 命令由 $ 符号开始。

例如

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

这将计算 Goldendict 历史文件中的行数。


这很聪明,我以这种方式进入 python -c,这是一个有趣的解决方法。感谢你的分享。 :)
e
estani

当我通过管道读取它的套接字时,我遇到了一些问题。当套接字关闭时,它开始在活动循环中返回空字符串。所以这是我的解决方案(我只在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


s
szeitlin

关于这一点:

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 是一个类似文件的对象
J
Jay

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