10

我需要在 Python 中将标准输入切换到非缓冲模式,这样我就可以从中读取单个字符。我设法让它工作,但现在标准输出被破坏了:不知何故,似乎在换行符之后,发出了一些空格字符,第一行为零,第二行为 3,第三行为 6,等等,像这样:

ASD
   ASD
      ASD

操作系统为 Ubuntu Linux 12.04,64 位版本,Python 版本为 3.2.3。

我怎样才能摆脱这种行为?

下面是我使用的代码:

import sys
import tty
import termios

fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
tty.setraw(sys.stdin)

for i in range(0, 10):
    print("ASD")

termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
4

3 回答 3

18

您遇到的问题是“raw”、“cooked”和“cbreak”模式之间的区别。这些模式是内核级终端驱动程序的模式,而不是您的应用程序代码或标准库或用户空间中的任何其他模式。这是引用这些的老式 Unix 方式。Posix 用一组更细粒度的属性取代了它们,尽管 Posix 属性通常与辅助函数一起以模仿旧的“raw”、“cooked”和“cbreak”模式的方式翻转。

在熟模式下,终端驱动程序本身具有内置的原始行编辑功能。它处理退格、单词擦除(基本上一次退格整个单词)和类似的事情。没有什么比处理箭头键或历史记录或类似的东西更复杂了。很原始。在这种模式下,您的程序在发送行尾 (eol) 字符之前永远不会从终端看到任何内容,然后您的程序会得到一整行,并且\n无论终端实际是什么,都会将行结尾转换为 Unix 标准做。此外,作为其中的一部分,终端驱动程序将输入的字符回显到终端,以便用户可以看到他们正在输入的内容。

在 'cooked' 模式下,内核级终端驱动程序也会进行一些输出转换。如果需要,其中一部分会\n变成。\r\n

此外,在“熟”模式下,终端驱动程序处理特殊字符,如 Control-C(向控制进程组发送 SIGINT(由 CPython 转换为 KeyboardInterrupt 异常))和 Control-Z(发送 SIGTSTP(如 SIGSTOP,但可以被捕获)到控制进程组)。

在“cbreak”模式下,不再进行行编辑。终端驱动程序立即将每个字符(或短字符序列,如箭头键的转义序列)提供给程序。这些字符不会回显到屏幕上,因此除非您的程序随后打印它们,否则用户将看不到它们。尽管终端驱动程序仍然处理特殊字符,如 Control-C 和 Control-Z,但它不再处理行编辑字符,如退格或字擦除字符(通常是 Control-W)。此外,一些输出处理仍然完成,因此驱动程序将 a\n变为 a \r\n

在“原始”模式下,不对输入或输出进行任何处理。没有特殊的字符处理,没有回显,没有转换\n\r\n,没有对 Control-Z 的处理,什么都没有。这取决于将终端置于原始模式的程序来完成这一切。

现在,您正在设置 的属性,sys.stdin因此您可能认为这不应该影响sys.stdout. 但是,事实上,您的两个文件描述符都会导致终端驱动程序的完全相同的“实例”。决定发生什么的是终端驱动程序的设置。sys.stdin因此,如果您通过、sys.stdout或什至更改这些设置都没有关系sys.stderr,它们都会更改相同的底层终端驱动程序实例,并且它们会影响所有其他设置。

当然,对于在程序启动之前由 shell 重定向的文件描述符来说,情况并非如此。

附带说明一下,您可以stty -a在命令行上使用 来查看所有这些标志的完整读数(包括哪些控制字符导致了cbreak 和cbreak 模式下的哪些信号)。

于 2017-05-12T05:15:16.683 回答
10

当我在寻找同样问题的答案时,谷歌把我带到了这里。没有回车的halex分享的线索帮助我寻找真相。我在 Chris 的 Wiki 上的帖子中找到了我的答案: https : //utcc.utoronto.ca/~cks/space/blog/unix/CBreakAndRaw,这让我在这里阅读了 tty.py 的来源:https://hg。 python.org/cpython/file/618ea5612e83/Lib/tty.py 这让我得出结论,如果目标是读取单个字符,而不是:

tty.setraw()

采用:

tty.setcbreak()
于 2016-05-21T03:44:32.413 回答
6

看起来你只做换行但没有回车。将您的打印更改为

print("ASD", end="\r\n")
于 2012-09-01T22:12:38.727 回答