2

I am wondering how to get Python to validate an input line and complete tasks using the input as it's being typed. For example:

alphabet = "abcdefghijklmnopqrstuvwxyz"

useralphabet = input("Please enter however much of the alphabet you know:")

while(useralphabet in alphabet):
    break

print("You know", 26 - len(useralphabet), "letters of the alphabet!")

Obviously I know this code won't work as intended, but I hope it demonstrates the idea of what I'm trying to do, i.e., get the user to input text until that they have entered is no longer part of the specified string.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
E. Davids
  • 21
  • 1
  • 1
    Does this answer your question? [How to read a single character from the user?](https://stackoverflow.com/questions/510357/how-to-read-a-single-character-from-the-user) – wjandrea Jan 02 '21 at 05:58

2 Answers2

0

The answer depends on your OS (operating system). Usually the OS hands over the input string to python only after you hit the ENTER key. If you need to do it as you type, you may need to invoke some system dependent calls to turn off input buffering.

See Python read a single character from the user for more on this

Community
  • 1
  • 1
sureshvv
  • 4,234
  • 1
  • 26
  • 32
0

Here is a working example (Tested with Python 3.8.6 on Linux). See this answer if you need to modify for other systems.

The hinput function reads chars as they are typed and calls on_char for each new character passing both the new char and the entire line.

In my example when the user types x the on_char function returns True which causes the hinput function to stop waiting for new input.

If the user types hello it is autocompleted to hello, world and also terminates hinput

import sys
import termios
import tty
from typing import Callable

def main():
    hinput("prompt: ", on_char)
    return 0

def on_char(ch: str, line: str) -> bool:
    if ch == 'x':
        sys.stdout.write('\n')
        sys.stdout.flush()
        return True
    if line+ch == 'hello':
        sys.stdout.write("%s, world\n" % ch)
        sys.stdout.flush()
        return True
    return False

def hinput(prompt: str=None, hook: Callable[[str,str], bool]=None) -> str:
    """input with a hook for char-by-char processing."""
    fd = sys.stdin.fileno()
    old = termios.tcgetattr(fd)
    inpt = ""
    while True:
        sys.stdout.write('\r')
        if prompt is not None:
            sys.stdout.write(prompt)
        sys.stdout.write(inpt)
        sys.stdout.flush()
            
        ch = None
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old)

        if hook is not None and hook(ch, inpt):
            break

        if ord(ch) == 0x7f: #BACKSPACE
            if len(inpt) > 0:
                sys.stdout.write('\b \b')
                inpt = inpt[:-1]
            continue

        if ord(ch) == 0x0d: #ENTER
            sys.stdout.write('\n')
            sys.stdout.flush()
            break

        if ch.isprintable():
            inpt += ch
            
    return inpt

if __name__ == '__main__':
    sys.exit(main())
Chris
  • 2,166
  • 1
  • 24
  • 37