Понадобилось мне самому побайтово читать и декодировать все, что поступает с stdin. Обычные символы, UTF8, специальные кнопки. С однобайтовыми символами проблем никаких, а вот с остальными по-человечески договориться не получается.
Начал с такого:
#!/usr/bin/python
import select
import sys
import tty
poller = select.poll()
poller.register( sys.stdin, select.POLLIN )
tty.setcbreak( sys.stdin )
while True:
events = poller.poll( 500 )
if events:
char = sys.stdin.read( 1 )
print ord( char )
Запускаю, жму abcd — все хорошо:
97 98 99 100
Жму Page up, и вместо
27 91 53 126
Имею только первый байт — 27. На остальные 3 байта poll не срабатывает до нажатия следующей клавиши.
Жму, например, Page up, потом enter, получается так:
27 <ничего> 91 53 126 10
То есть после нажатия второй клавиши poll срабатывает 4 раза: 3 раза для оставшихся от Page up байт и один раз для enter.
Пока придумал 2 решения. Очень плохое и сомнительное.
Очень плохое решение
Повесить на stdin non-block и читать себе, весело пожирая процессор.
import sys
import tty
import fcntl
import os
fd = sys.stdin.fileno()
fl = fcntl.fcntl( fd, fcntl.F_GETFL )
fcntl.fcntl( fd, fcntl.F_SETFL, fl | os.O_NONBLOCK )
tty.setcbreak( sys.stdin )
while True:
try:
c = sys.stdin.read( 1 )
print ord( c )
except:
pass
Сомнительное решение
Сразу после получения первого символа начинать декодирование utf по
вот этой табличке, если надо, считывать еще символов.
Сомнения, в общем-то, возникают в двух моментах:
- А вдруг, следующего символа не будет и sys.std.read заблокируется до следующего символа? Пока не придумал почему такое может случиться, но кажется вполне реальным. Вешать non-block только ради этого очень уж не хочется.
- Нужно дополнительно обрабатывать первый символ, если он равен 27, чтобы считывать всякие спецсимволы. Никакой удобной таблички наподобие той, что есть для UTF, я не нашел.
Все, что мне надо — разбить входной поток байтов на мультибайтовые символы.