2014-02-15 3 views
6

Я работаю над ботом для соревнования, который получает свой «вход через sys.stdin» и использует печать() для вывода на Python. У меня есть следующие:Ввод данных из sys.stdin, неблокирующий

import sys 

def main(): 
    while True: 
     line = sys.stdin.readline() 
     parts = line.split() 
     if len(parts) > 0: 
      # do stuff 

Проблема заключается в том, что вход поступает через поток и используя вышеупомянутые блоки мне печатать что-либо назад, пока поток не будет закрыт. Что я могу сделать, чтобы сделать эту работу?

+0

[возможно] (http://stackoverflow.com/questions/ 8416586/turn-off-buffering) a [duplicate] (http://stackoverflow.com/questions/107705/python-output-buffering) –

+0

Неблокирование на stdin либо не работает, либо не работает очень надежно. Разрешено ли использование потоков/многопроцессорности? Причина, которая должна работать –

ответ

5

При повороте перекрытием вы можете только читать посимвольно. Таким образом, нет возможности получить readline() для работы в неблокирующем контексте. Я предполагаю, что вы просто хотите прочитать нажатия клавиш для управления роботом.

Мне не повезло, используя select.select() на Linux и создал способ с настройкой настроек termios. Таким образом, это Linux конкретные, но работает для меня:

old_settings=None 

def init_anykey(): 
    global old_settings 
    old_settings = termios.tcgetattr(sys.stdin) 
    new_settings = termios.tcgetattr(sys.stdin) 
    new_settings[3] = new_settings[3] & ~(termios.ECHO | termios.ICANON) # lflags 
    new_settings[6][termios.VMIN] = 0 # cc 
    new_settings[6][termios.VTIME] = 0 # cc 
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings) 

@atexit.register 
def term_anykey(): 
    global old_settings 
    if old_settings: 
     termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) 

def anykey(): 
    ch_set = [] 
    ch = os.read(sys.stdin.fileno(), 1) 
    while ch != None and len(ch) > 0: 
     ch_set.append(ord(ch[0])) 
     ch = os.read(sys.stdin.fileno(), 1) 
    return ch_set; 

init_anykey() 
while True: 
    key = anykey() 
    if key != None: 
     print key 
    else: 
     time.sleep(0.1) 

Лучше Windows, или кросс-платформенный ответ здесь: Python nonblocking console input

-4

Используйте генератор - к счастью sys.stdin уже является генератором!

Генератор позволяет работать с бесконечным потоком. Всегда, когда вы вызываете его, он возвращает следующий элемент. Для создания генератора вам понадобится ключевое слово yield.

for line in sys.stdin: 
    print line 

    if a_certain_situation_happens: 
     break   

Не забудьте поместить break заявление в петлю, если определенная, хотела ситуация происходит.

Вы можете найти более подробную информацию о генераторах на:

+0

Не существуют ли другие факторы? Например, если поток буферизируется по строке или блоку? –

+1

sys.stdin уже является генератором, поэтому вы можете просто выполнить 'для строки в sys.stdin: ...' или использовать новый модуль 'fileinput'. Тем не менее, они не блокируют. – gatoatigrado

5
#----------------------------------------------------------------------- 
# Get a character from the keyboard. If Block is True wait for input, 
# else return any available character or throw an exception if none is 
# available. Ctrl+C isn't handled and continues to generate the usual 
# SIGINT signal, but special keys like the arrows return the expected 
# escape sequences. 
# 
# This requires: 
# 
# import sys, select 
# 
# This was tested using python 2.7 on Mac OS X. It will work on any 
# Linux system, but will likely fail on Windows due to select/stdin 
# limitations. 
#----------------------------------------------------------------------- 

def GetChar(Block=True): 
    if Block or select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []): 
    return sys.stdin.read(1) 
    raise error('NoChar') 
+0

Я верю, что пробовал это на Linux некоторое время назад, и я не думаю, что это сработало. Тем не менее, на Mac я сейчас, это определенно не работает. Является ли блок истинным или ложным, он по-прежнему блокируется. Кроме того, пользователь должен нажать клавишу ввода, чтобы освободить «наводнение» создаваемых символов. Возможно, попробуйте установить режим ввода в raw ('tty.setraw()'), но после этого вы должны установить его в готовый режим после этого. – dylnmc

0

Вы можете использовать селекторы для ручки I/O мультиплексирование:

https://docs.python.org/3/library/selectors.html

Попробуйте это:

#! /usr/bin/python3 

import sys 
import fcntl 
import os 
import selectors 

# set sys.stdin non-blocking 
orig_fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL) 
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, orig_fl | os.O_NONBLOCK) 

# function to be called when enter is pressed 
def got_keyboard_data(stdin): 
    print('Keyboard input: {}'.format(stdin.read())) 

# register event 
m_selector = selectors.DefaultSelector() 
m_selector.register(sys.stdin, selectors.EVENT_READ, got_keyboard_data) 

while True: 
    sys.stdout.write('Type something and hit enter: ') 
    sys.stdout.flush() 
    for k, mask in m_selector.select(): 
     callback = k.data 
     callback(k.fileobj) 

Приведенный выше код будет держать на линии

for k, mask in m_selector.select(): 

, пока не произойдет зарегистрированное событие, возвращая selector_key экземпляр (к) и маску отслеживаемых событий.

В приведенном выше примере мы зарегистрировали только одно событие (Enter нажатие клавиши):

m_selector.register(sys.stdin, selectors.EVENT_READ, got_keyboard_data) 

Селектор ключа экземпляра определяется следующим образом:

abstractmethod register(fileobj, events, data=None) 

Таким образом, метод регистра устанавливает к.данные, как наша функция обратного вызова got_keyboard_data, и называет его, когда Enter нажата клавиша:

callback = k.data 
    callback(k.fileobj) 

Более полный пример (и, надеюсь, более полезным) будет мультиплекс STDIN данные пользователя с Incomming соединений от сети:

import selectors 
import socket 
import sys 
import os 
import fcntl 

m_selector = selectors.DefaultSelector() 

# set sys.stdin non-blocking 
def set_input_nonblocking(): 
    orig_fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL) 
    fcntl.fcntl(sys.stdin, fcntl.F_SETFL, orig_fl | os.O_NONBLOCK) 

def create_socket(port, max_conn): 
    server_addr = ('localhost', port) 
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    server.setblocking(False) 
    server.bind(server_addr) 
    server.listen(max_conn) 
    return server 

def read(conn, mask): 
    global GO_ON 
    client_address = conn.getpeername() 
    data = conn.recv(1024) 
    print('Got {} from {}'.format(data, client_address)) 
    if not data: 
     GO_ON = False 

def accept(sock, mask): 
    new_conn, addr = sock.accept() 
    new_conn.setblocking(False) 
    print('Accepting connection from {}'.format(addr)) 
    m_selector.register(new_conn, selectors.EVENT_READ, read) 

def quit(): 
    global GO_ON 
    print('Exiting...') 
    GO_ON = False 


def from_keyboard(arg1, arg2): 
    line = arg1.read() 
    if line == 'quit\n': 
     quit() 
    else: 
     print('User input: {}'.format(line)) 

GO_ON = True 
set_input_nonblocking() 

# listen to port 10000, at most 10 connections 
server = create_socket(10000, 10) 

m_selector.register(server, selectors.EVENT_READ, accept) 
m_selector.register(sys.stdin, selectors.EVENT_READ, from_keyboard) 

while GO_ON: 
    sys.stdout.write('>>> ') 
    sys.stdout.flush() 
    for k, mask in m_selector.select(): 
     callback = k.data 
     callback(k.fileobj, mask) 


# unregister events 
m_selector.unregister(sys.stdin) 

# close connection 
server.shutdown() 
server.close() 

# close select 
m_selector.close() 

Вы можете протестировать использование двух терминалов. первый терминал:

$ python3 test.py 
>>> bla 

открыт другой терминал и запустить:

$ nc localhost 10000 
hey! 

назад к первому

>>> qwerqwer  

Результат (видно на главном терминале):

$ python3 test.py 
>>> bla 
User input: bla 

>>> Accepting connection from ('127.0.0.1', 39598) 
>>> Got b'hey!\n' from ('127.0.0.1', 39598) 
>>> qwerqwer  
User input: qwerqwer 

>>> 
+0

Пожалуйста, добавьте объяснение с вашим сообщением, чтобы оно было понятным и понятным для будущих посетителей – Simon

Смежные вопросы