Python — чтение c stdin

Понадобилось мне самому побайтово читать и декодировать все, что поступает с 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 по вот этой табличке, если надо, считывать еще символов.
Сомнения, в общем-то, возникают в двух моментах:
  1. А вдруг, следующего символа не будет и sys.std.read заблокируется до следующего символа? Пока не придумал почему такое может случиться, но кажется вполне реальным. Вешать non-block только ради этого очень уж не хочется.
  2. Нужно дополнительно обрабатывать первый символ, если он равен 27, чтобы считывать всякие спецсимволы. Никакой удобной таблички наподобие той, что есть для UTF, я не нашел.


Все, что мне надо — разбить входной поток байтов на мультибайтовые символы.
  • Вопрос задан
  • 14531 просмотр
Пригласить эксперта
Ответы на вопрос 3
@gribozavr
Необходимо перевести файловый дескриптор в неблокирующий режим:
#!/usr/bin/python
import select
import sys
import os
import tty
import fcntl

poller = select.poll()
poller.register(sys.stdin, select.POLLIN)

tty.setcbreak(sys.stdin)

fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)

while True:
        events = poller.poll(500)
        if events:
                for char in sys.stdin.read():
                        print ord(char)
Ответ написан
enchantner
@enchantner
А если попробовать через codecs? Что-то в таком духе:

import codecs
import locale
import sys

locale.setlocale(locale.LC_ALL, '')

lang, encoding = locale.getdefaultlocale()
sys.stdin = codecs.getreader(encoding)(sys.stdin)

print 'From stdin:', repr(sys.stdin.read())


Можно почитать тут: www.doughellmann.com/PyMOTW/codecs/index.html
Ответ написан
Kindman
@Kindman
Можно перевести в неблокирующий режим и внутри цикла опроса ставить паузу на 30 миллисекунд.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы