如何在Python中从stdin读取单个字符?(处理Arduino串口数据)
在Python中从stdin读取单个字符(适配Arduino串口数据场景)
刚好我之前也处理过类似的串口数据读取需求,知道默认的行缓冲机制会给单字符读取带来麻烦,下面分场景给你几种可行的方案:
核心问题:默认stdin的行缓冲
默认情况下,终端的stdin是行缓冲模式——也就是说,只有当按下回车时,输入的内容才会被传给Python程序,这显然不符合你读取单个串口字符的需求。所以我们需要先修改终端的缓冲模式,或者用平台特定的方法绕过缓冲。
方案1:Linux/macOS 下用 termios 模块修改终端属性
这个方法能直接修改终端的输入模式,让它变成字符缓冲(输入一个字符就立刻传给程序),非常适合串口数据的实时读取。
示例代码:
import sys import termios import tty def get_single_char(): # 保存当前终端属性 old_attr = termios.tcgetattr(sys.stdin) try: # 设置终端为字符缓冲模式 tty.setcbreak(sys.stdin.fileno()) # 读取单个字符 char = sys.stdin.read(1) return char finally: # 恢复终端原属性,避免影响后续操作 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_attr) # 针对你的Arduino场景:读取<和>包裹的内容 def read_arduino_data(): buffer = [] while True: char = get_single_char() if char == '<': # 遇到起始符,清空缓冲区开始收集 buffer = [] elif char == '>': # 遇到结束符,返回收集到的内容 return ''.join(buffer) else: # 中间字符加入缓冲区 buffer.append(char) # 测试:读取数据并写入文件 if __name__ == "__main__": with open("arduino_data.txt", "a") as f: while True: data = read_arduino_data() print(f"收到数据:{data}") f.write(data + "\n") f.flush()
方案2:Windows 下用 msvcrt 模块
Windows系统没有termios,我们可以用内置的msvcrt模块来实现单字符读取,它会直接从控制台读取单个字符,不需要处理缓冲问题。
示例代码:
import sys import msvcrt def get_single_char(): # 读取单个字符,不需要回车 return msvcrt.getch().decode('utf-8') # 同样的Arduino数据读取逻辑 def read_arduino_data(): buffer = [] while True: char = get_single_char() if char == '<': buffer = [] elif char == '>': return ''.join(buffer) else: buffer.append(char) # 写入文件的逻辑和上面一致 if __name__ == "__main__": with open("arduino_data.txt", "a") as f: while True: data = read_arduino_data() print(f"收到数据:{data}") f.write(data + "\n") f.flush()
方案3:跨平台简化版(适合串口转发场景)
如果你用的串口工具已经把串口数据直接转发到stdin(比如没有额外的行缓冲),也可以尝试用sys.stdin.read(1)直接读取,但要注意关闭Python的缓冲:
import sys # 关闭stdin的缓冲 sys.stdin = open(sys.stdin.fileno(), 'rb', buffering=0) def get_single_char(): return sys.stdin.read(1).decode('utf-8') # 后续的读取逻辑和前面一致
不过这个方法的兼容性不如前两种,因为有些终端环境还是会强制行缓冲,所以优先推荐前两种平台特定的方案。
额外提示(针对你的Arduino场景)
- 确保Arduino的串口波特率和转发工具的波特率一致,避免出现乱码;
- 如果你的Arduino发送的是二进制数据,记得把读取的字节直接处理,不要解码成字符串;
- 写入文件时记得调用
flush(),避免数据因为文件缓冲而延迟写入。
内容的提问来源于stack exchange,提问作者Philipp
相关产品推荐
相关产品推荐

