你如何从Python中读取stdin中的单个字符

时间:2018-05-08 17:49:02

标签: python stdin

关于如何从python中的stdin读取有很多很好的答案,但是我无法找到关于阅读单个字符而不是整行的任何内容。让我解释一下:

我需要读取arduino在串口上发送的信息,然后转发给stdin。处理该信息并将其存储在文本文件中。我在arduino上编写了程序,所以我可以改变信息的发送方式。计划是以开始 - (<)和结束字符(>)发送信息,因此它看起来像这样:<height:2342>

还会写出很多不相关的数据,这就是为什么我决定使用上面的表格,所以python脚本可以检测相关信息并捕获它。

我的python脚本会单独检查每个字符的起始字符<,最好是在输入时,然后捕获信息,直到收到>。我尝试使用inputchar = sys.stdin.read(1)获取输入。但是这里的问题是,它永远从stdin读取,直到捕获换行符(\n),然后返回输入的第一个字符。

我希望此函数在发送到stdin后立即返回输入的字符,而不是等待换行符。我怎么做到这一点?

平台:Raspberry Pi Zero W,Raspbian Jessy,Python 2.7

我知道我可以使用 inputline = sys.stdin.readline() 并更改Arduino程序以在信息之后发送换行符。然后分析整行(可能很长)并提取信息。但我觉得这不是一个干净的方法。

串口更新:遗憾的是,我无法直接从python访问串口,因为还有第二个python脚本需要写入串口。由于只有一个可以访问端口,因此解决方案是将串行端口重定向到stdinstdout。请参阅我的问题Access one Serial Port with two Python Scripts

4 个答案:

答案 0 :(得分:1)

这是因为您terminal is in cooked mode。你可以用例如tty.setcbreakcurses.cbreak禁用线路缓冲。这是一个Unix的东西,特别是Python特有的。

示例:

import sys, tty
tty.setcbreak(sys.stdin.fileno())

请注意,这会产生其他影响,例如禁用echo,并在程序退出时保持不变。通常,诸如curses上下文管理器之类的更高级别接口用于处理密钥解析(箭头键,例如,发送转义序列)和清理。

Python之外的主要命令行工具是stty

答案 1 :(得分:0)

sys.stdin.read(1)是一种正确的方法。

在点击输入之前,sys.stdin处没有输入。该行缓冲是在您的程序之外执行的,如果您想使用sys.stdin,则无法对其执行任何操作。

答案 2 :(得分:0)

我无法重现您描述的行为......

在我的Raspbian上执行 - 在RPI2上:

$ cat a.py
#!/usr/bin/env python2
import sys

while True:
    print "Got", ord(sys.stdin.read(1))


$ echo -n "Test"  | hexdump -C
00000000  54 65 73 74                                       |Test|
00000004

$ # No newline in the data sent by echo, as shown by hexdump

$ echo -n "Test"  | python2 a.py
Got 84
Got 101
Got 115
Got 116
Got
Traceback (most recent call last):
  File "a.py", line 5, in <module>
    print "Got", ord(sys.stdin.read(1))
TypeError: ord() expected a character, but string of length 0 found

基本上,sys.stdin.read(1)返回stdin发送的任何字符,而不等待任何换行符。

答案 3 :(得分:0)

另一种方法是将TTY设置为原始模式。我想确定按键的持续时间,并且需要这样做,这样我就不必按Enter键:

#!/usr/bin/env python3

import datetime
import sys
import tty
import termios


# read a single character on a unix system
# https://exceptionshub.com/python-read-a-single-character-from-the-user.html
def read_unix(count: int):
    fd = sys.stdin.fileno()

    old_settings = termios.tcgetattr(fd)
    try:
        tty.setraw(fd)
        ch = sys.stdin.read(count)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

    return ch


while True:
    start = datetime.datetime.now()
    char = read_unix(1)
    end = datetime.datetime.now()

    delta = end - start
    print(f'char={char}, delta={delta}')