是否有办法从用户输入中读取单个字符?例如,他们在终端上按下一个键,它就会返回(有点像getch())。我知道Windows中有这个功能,但我想要跨平台的东西。
当前回答
ActiveState的配方似乎包含一个小错误,“posix”系统,防止Ctrl-C中断(我使用Mac)。如果我把下面的代码放在我的脚本:
while(True):
print(getch())
我将永远无法用Ctrl-C终止脚本,我必须杀死我的终端才能退出。
我认为下面这句话是原因,而且它也太残酷了:
tty.setraw(sys.stdin.fileno())
除此之外,tty包实际上并不需要,termios就足以处理它了。
下面是改进的代码,为我工作(Ctrl-C将中断),与额外的getche函数,当你输入的字符回显:
if sys.platform == 'win32':
import msvcrt
getch = msvcrt.getch
getche = msvcrt.getche
else:
import sys
import termios
def __gen_ch_getter(echo):
def __fun():
fd = sys.stdin.fileno()
oldattr = termios.tcgetattr(fd)
newattr = oldattr[:]
try:
if echo:
# disable ctrl character printing, otherwise, backspace will be printed as "^?"
lflag = ~(termios.ICANON | termios.ECHOCTL)
else:
lflag = ~(termios.ICANON | termios.ECHO)
newattr[3] &= lflag
termios.tcsetattr(fd, termios.TCSADRAIN, newattr)
ch = sys.stdin.read(1)
if echo and ord(ch) == 127: # backspace
# emulate backspace erasing
# https://stackoverflow.com/a/47962872/404271
sys.stdout.write('\b \b')
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, oldattr)
return ch
return __fun
getch = __gen_ch_getter(False)
getche = __gen_ch_getter(True)
引用:
https://pypi.python.org/pypi/getch
其他回答
(目前)排名第一的答案(带有ActiveState代码)过于复杂。当仅仅一个函数就足够了时,我不认为有理由使用类。下面是两个实现,它们实现了相同的功能,但代码可读性更强。
这两个实现:
可以在python2或python3中正常工作 可以在Windows, OSX和Linux上工作 只读取一个字节(即,它们不等待换行符) 不要依赖任何外部库 是自包含的(没有函数定义之外的代码)
版本1:易读且简单
def getChar():
try:
# for Windows-based systems
import msvcrt # If successful, we are on Windows
return msvcrt.getch()
except ImportError:
# for POSIX-based systems (with termios & tty support)
import tty, sys, termios # raises ImportError if unsupported
fd = sys.stdin.fileno()
oldSettings = termios.tcgetattr(fd)
try:
tty.setcbreak(fd)
answer = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)
return answer
版本2:避免重复导入和异常处理:
[编辑]我错过了ActiveState代码的一个优点。如果您计划多次读取字符,该代码可以避免在类unix系统上重复Windows导入和ImportError异常处理的成本(可以忽略不计)。虽然你可能应该更关心代码的可读性,而不是可以忽略的优化,这里有一个替代方案(它类似于Louis的答案,但getChar()是自包含的),它的功能与ActiveState代码相同,更可读:
def getChar():
# figure out which function to use once, and store it in _func
if "_func" not in getChar.__dict__:
try:
# for Windows-based systems
import msvcrt # If successful, we are on Windows
getChar._func=msvcrt.getch
except ImportError:
# for POSIX-based systems (with termios & tty support)
import tty, sys, termios # raises ImportError if unsupported
def _ttyRead():
fd = sys.stdin.fileno()
oldSettings = termios.tcgetattr(fd)
try:
tty.setcbreak(fd)
answer = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)
return answer
getChar._func=_ttyRead
return getChar._func()
使用上述getChar()版本的示例代码:
from __future__ import print_function # put at top of file if using Python 2
# Example of a prompt for one character of input
promptStr = "Please give me a character:"
responseStr = "Thank you for giving me a '{}'."
print(promptStr, end="\n> ")
answer = getChar()
print("\n")
print(responseStr.format(answer))
我相信这是一个最优雅的解决方案。
import os
if os.name == 'nt':
import msvcrt
def getch():
return msvcrt.getch().decode()
else:
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
def getch():
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
然后在代码中使用它:
if getch() == chr(ESC_ASCII_VALUE):
print("ESC!")
试试这个:http://home.wlu.edu/~levys/software/kbhit.py 它是非阻塞的(这意味着您可以有一个while循环并检测按键而无需停止它)和跨平台的。
import os
# Windows
if os.name == 'nt':
import msvcrt
# Posix (Linux, OS X)
else:
import sys
import termios
import atexit
from select import select
class KBHit:
def __init__(self):
'''Creates a KBHit object that you can call to do various keyboard things.'''
if os.name == 'nt':
pass
else:
# Save the terminal settings
self.fd = sys.stdin.fileno()
self.new_term = termios.tcgetattr(self.fd)
self.old_term = termios.tcgetattr(self.fd)
# New terminal setting unbuffered
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
# Support normal-terminal reset at exit
atexit.register(self.set_normal_term)
def set_normal_term(self):
''' Resets to normal terminal. On Windows this is a no-op.
'''
if os.name == 'nt':
pass
else:
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
def getch(self):
''' Returns a keyboard character after kbhit() has been called.
Should not be called in the same program as getarrow().
'''
s = ''
if os.name == 'nt':
return msvcrt.getch().decode('utf-8')
else:
return sys.stdin.read(1)
def getarrow(self):
''' Returns an arrow-key code after kbhit() has been called. Codes are
0 : up
1 : right
2 : down
3 : left
Should not be called in the same program as getch().
'''
if os.name == 'nt':
msvcrt.getch() # skip 0xE0
c = msvcrt.getch()
vals = [72, 77, 80, 75]
else:
c = sys.stdin.read(3)[2]
vals = [65, 67, 66, 68]
return vals.index(ord(c.decode('utf-8')))
def kbhit(self):
''' Returns True if keyboard character was hit, False otherwise.
'''
if os.name == 'nt':
return msvcrt.kbhit()
else:
dr,dw,de = select([sys.stdin], [], [], 0)
return dr != []
使用这个的例子:
import kbhit
kb = kbhit.KBHit()
while(True):
print("Key not pressed") #Do something
if kb.kbhit(): #If a key is pressed:
k_in = kb.getch() #Detect what key was pressed
print("You pressed ", k_in, "!") #Do something
kb.set_normal_term()
或者您可以使用PyPi中的getch模块。但是这会阻塞while循环
值得一试的是readchar库,它在一定程度上基于其他答案中提到的ActiveState配方(但从那以后已经有了很长的路要走)。
安装:
python -m pip install readchar
用法:
import readchar
print('Reading a char:')
print(repr(readchar.readchar()))
print('Reading a key:')
print(repr(readchar.readkey()))
使用Python 3.9在Windows和Linux上测试。
Windows和Linux之间的键码并不总是相同的,但是库提供了特定于平台的定义,如readchar.key。F1来帮忙。
由于Linux将大多数特殊键报告为转义序列(从\x1b开始),如果您按下实际的转义键(终端将其报告为单独的\x1b), readkey()就会混淆。不幸的是,这是一个常见的Unix问题,没有真正可靠的解决方案。
注意,当readkey在Ctrl+C上触发KeyboardInterrupt时(参见readchar.config),其他Linux信号键(例如Ctrl+D和Ctrl+Z)被捕获并返回(分别为'\x04'和'\x1a'),这可能是也可能不是可取的。
如果我在做一些复杂的事情,我会使用诅咒来读取键。但很多时候,我只是想要一个简单的Python 3脚本,使用标准库,可以读取方向键,所以我这样做:
import sys, termios, tty
key_Enter = 13
key_Esc = 27
key_Up = '\033[A'
key_Dn = '\033[B'
key_Rt = '\033[C'
key_Lt = '\033[D'
fdInput = sys.stdin.fileno()
termAttr = termios.tcgetattr(0)
def getch():
tty.setraw(fdInput)
ch = sys.stdin.buffer.raw.read(4).decode(sys.stdin.encoding)
if len(ch) == 1:
if ord(ch) < 32 or ord(ch) > 126:
ch = ord(ch)
elif ord(ch[0]) == 27:
ch = '\033' + ch[1:]
termios.tcsetattr(fdInput, termios.TCSADRAIN, termAttr)
return ch
推荐文章
- pylab和pyplot的区别是什么?
- Argparse:确定使用了哪个子解析器
- django导入错误-没有core.management模块
- 在芹菜中检索队列中的任务列表
- 使用beautifulsoup提取属性值
- 如何禁用标准错误流的日志记录?
- 用Matplotlib在Python中绘制时间
- 类中的Python装饰器
- 在Python中锁定文件
- 得到熊猫栏目的总数
- 从pandas DataFrame中删除名称包含特定字符串的列
- Mock vs MagicMock
- 如何阅读一个。xlsx文件使用熊猫库在iPython?
- 如何访问熊猫组由数据帧按键
- Pandas和NumPy+SciPy在Python中的区别是什么?