当前位置:  开发笔记 > 编程语言 > 正文

Python从用户读取单个字符

如何解决《Python从用户读取单个字符》经验,为你挑选了7个好方法。

有没有办法从用户输入读取一个单个字符?例如,他们在终端按一个键然后返回(有点像getch()).我知道Windows中有一个功能,但我想要一些跨平台的功能.



1> tehvan..:

这是一个指向如何在Windows,Linux和OSX中读取单个字符的网站的链接:http://code.activestate.com/recipes/134892/

class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()


class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()


getch = _Getch()


代码看起来很短,你可以只包括它,但+1代表如此快速地找到一个好的(跨平台)答案.
@Seismoid:要求宽恕通常被认为更好,请参阅http://stackoverflow.com/questions/12265451/ask-forgiveness-not-permission-explain
我不喜欢如何使用`ImportError`异常,就像某种if语句一样; 为什么不调用platform.system()来检查操作系统?
它能很好地处理非拉丁语(例如西里尔字母)的字母吗?我遇到了问题而无法弄明白,如果这是我的错误,或不是.
在OS X上不起作用:"old_settings = termios.tcgetattr(fd)""termios.error:(25,'不恰当的设备ioctl')"
为什么这些类会覆盖`__call__`而不是普通方法?
这些只是函数的伪类的使用是可怕的恕我直言。为什么不为每个模块组提供一个简单函数,并为一个函数确定并返回要使用的函数呢?

2> Yuval Adam..:
sys.stdin.read(1)

基本上会从STDIN读取1个字节.

如果您必须使用不等待的方法,\n您可以使用前面答案中建议的代码:

class _Getch:
    """Gets a single character from standard input.  Does not echo to the screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()


class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()


getch = _Getch()

(摘自 http://code.activestate.com/recipes/134892/)


我觉得奇怪,sys.stdin.read(1)等待一个\n,大声笑.谢谢你的提交.
@Evan,这是因为默认情况下python处于行缓冲模式
请注意,此代码阻止您使用^ C或^ D!
一个字符还是一个字节?那不一样.
@EvanFosmark:sys.stdin.read(1)不一定等待\n,它是终端程序决定何时向你的程序发送其他字符不会写它们直到它看到'\n' - 怎么会你可以按退格键并更正你输入的内容吗?(严肃的答案是 - 教python程序实现行控制,保留缓冲区,处理退格键,但这是一个不同的世界,你可能不想在"阅读一个角色"时买进,并且可以使你的线处理与您系统上的所有其他程序不同.)
@Seismoid [EAFP](http://stackoverflow.com/questions/11360858/what-is-the-eafp-principle-in-python)

3> Louis..:

在两个答案中逐字引用的ActiveState 配方是过度设计的.它可以归结为:

def _find_getch():
    try:
        import termios
    except ImportError:
        # Non-POSIX. Return msvcrt's (Windows') getch.
        import msvcrt
        return msvcrt.getch

    # POSIX system. Create and return a getch that manipulates the tty.
    import sys, tty
    def _getch():
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(fd)
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

    return _getch

getch = _find_getch()



4> Søren Løvbor..:

另外值得一试的是readchar库,它部分基于其他答案中提到的ActiveState配方.

安装:

pip install readchar

用法:

import readchar
print("Reading a char:")
print(repr(readchar.readchar()))
print("Reading a key:")
print(repr(readchar.readkey()))

使用Python 2.7在Windows和Linux上测试.

在Windows上,只有映射到字母或ASCII控制代码键的支持(Backspace,Enter,Esc,Tab,Ctrl+ 字母).在GNU/Linux(取决于具体终端上,也许?),你也可以得到Insert,Delete,Pg Up,Pg Dn,Home,End和键...但随后,有分离的这些特殊键问题.F nEsc

警告:像在这里的大多数(?全部)答案,信号键,如Ctrl+ C,Ctrl+ DCtrl+ Z被抓获和返回(如'\x03','\x04''\x1a'分别); 你的程序很难中止.


也适用于Linux上的Python 3.比getch好多了,因为readchar允许在等待键(通过线程或asyncio)时打印到stdout.

5> Tyler..:

另一种方法:

import os
import sys    
import termios
import fcntl

def getch():
  fd = sys.stdin.fileno()

  oldterm = termios.tcgetattr(fd)
  newattr = termios.tcgetattr(fd)
  newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
  termios.tcsetattr(fd, termios.TCSANOW, newattr)

  oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
  fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)

  try:        
    while 1:            
      try:
        c = sys.stdin.read(1)
        break
      except IOError: pass
  finally:
    termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
  return c

来自这篇博文.



6> kiri..:

此代码基于此处,如果按下Ctrl+ CCtrl+ ,将正确引发KeyboardInterrupt和EOFError D.

应该适用于Windows和Linux.OS X版本可从原始源获得.

class _Getch:
    """Gets a single character from standard input.  Does not echo to the screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): 
        char = self.impl()
        if char == '\x03':
            raise KeyboardInterrupt
        elif char == '\x04':
            raise EOFError
        return char

class _GetchUnix:
    def __init__(self):
        import tty
        import sys

    def __call__(self):
        import sys
        import tty
        import termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()


getch = _Getch()



7> 小智..:

(当前)排名靠前的答案(使用ActiveState代码)过于复杂.当一个单纯的功能足够时,我没有看到使用类的理由.下面是两个实现相同但具有更多可读代码的实现.

这两个实现:

    在Python 2或Python 3中工作得很好

    适用于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))


在同时等待键(多线程)的同时打印消息时,我遇到了tty.setraw()的问题。长话短说,我发现使用tty.setcbreak()可以让您获得单个字符而不会破坏所有其他普通的东西。此[answer]中的长话[http://stackoverflow.com/questions/12231794/python-in-raw-mode-stdin-print-adds-spaces/37358649#37358649)
推荐阅读
小色米虫_524
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有