【问题标题】:How do you read from stdin?你如何从标准输入读取?
【发布时间】:2023-07-25 10:39:01
【问题描述】:

我正在尝试做一些code golf 挑战,但它们都需要从stdin 获取输入。我如何在 Python 中获得它?

【问题讨论】:

    标签: python stdin


    【解决方案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 是一个类似文件的对象,如果您想读取所有内容,或者您​​想读取所有内容并自动用换行符分割,您可以在其上调用函数readreadlines。 (您需要 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 程序,例如 catgrepsed

      【讨论】:

        【解决方案9】:

        试试这个:

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

        并检查:

        $ echo "Hello World" | python myFile.py
        

        【讨论】:

          【解决方案10】:

          您可以从标准输入读取,然后将输入存储到 "data" 中,如下所示:

          data = ""
          for line in sys.stdin:
              data += line
          
          【解决方案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()) 一样使用。

              sysfileinput 当然必须分别导入。

              Quick sys.stdin 示例兼容 Python 2 和 3、Windows、Unix

              您只需要从sys.stdinread,例如,如果您将数据通过管道传输到标准输入:

              $ 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
              

              打高尔夫球的文件描述符

              由于 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 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 文档那样简单地重命名为infiledocs.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)
                        

                        【讨论】:

                          最近更新 更多