2015-01-02 9 views
1

我的程序(a "TRAC Processor")使用逐個字符的輸入。我正在爲字符串實現類似於readline的輸入功能,這些字符串以enter(通常爲')以外的字符終止,並且本身可能是多行的。所以我在輸入字符之間輸出終端轉義序列,包括查詢終端仿真器(光標位置和屏幕大小)的轉義序列。爲了實現跨平臺的單字符輸入,我使用了http://code.activestate.com/recipes/134892/,這非常有幫助。需要逐字符鍵盤輸入,與粘貼和ANSI轉義序列交互良好

這適用於粘貼......直到我需要在粘貼的第一個字符後得到終端響應。似乎粘貼的文本正在與對轉義序列的響應混合在一起。我想我會通過在啓動轉義序列查詢之前刷新輸入緩衝區來修復它:wait 10ms,並且如果沒有輸入繼續;如果有輸入,請緩衝並等待,直到沒有輸入。基於this post,我試圖用select()來輪詢stdin。好主意,但它不起作用,並且產生了非常奇怪的行爲。我在這個問題的原始版本中發佈了這個奇怪的行爲,認爲我誤解了select,並且有一種解決方法。似乎沒有,但我找到了另一種方法來刷新(並保存)輸入流。我決定保留這個問題,並將該方法作爲答案發布。

select()的問題解釋爲here。在粘貼的第一個字符之後,其他字符已經被緩存,並且select僅在有超出已經緩存的新輸入時才返回新輸入。我無法讓自己刪除由此產生的MWE,所以你可以在下面看到它。

不幸的是,在該帖子中提出的答案要麼不適合我,要麼需要更多的解釋。 @slowdog建議使用未緩衝的輸入(os.read(stdin.fileno(),1)而不是stdin.read(1))。這解決了選擇的問題,但它打破了粘貼:看起來第一個粘貼後的所有字符都被緩衝了,不管是什麼,所以你永遠不會看到它們。它似乎也不適用於似乎也被緩衝的逃逸序列響應。這也很煩人,因爲你需要刷新輸出緩衝區,但這並不是很糟糕。 @Omnafarious在一篇評論中說:「儘管如此,另一種處理Python緩衝問題的方法是簡單地做一個無參數讀取,它應該讀取當前可用的所有內容。」這正是我所做的,如下所示,但「簡單」卻不是那麼簡單。還有另一種解決方案here,但我想在那裏必須是一種無需線程的方式。

順便說一句,有一個相對簡單的解決方法,因爲事實證明,粘貼並不隨機地散佈在對轉義序列的響應中。在轉義序列響應之前,粘貼的所有剩餘部分都會被讀取,因此,當您正在查找轉義序列響應(本身以轉義開頭)時,您可以緩存轉義前讀取的所有字符,並處理他們以後。如果您可能在終端上鍵入ESC字符,則這隻會失敗。無論如何,到目前爲止,我非常想解決這個問題,並且我認爲其他人可能會找到有價值的答案。

反正FWIW這裏是我的select問題MWE,剛剛相呼應的文本,而不是緩衝它:

def flush(): 
    import sys, tty, termios 
    from select import select 
    tty.setraw(sys.stdin.fileno()) 
    while True: 
     rlist, wlist, xlist = select([sys.stdin], [], [], 1) 
     if rlist == []: return 
     sys.stdout.write(sys.stdin.read(1)) 

粘貼此到Python提示符(2.7.9),並把另一個空白行中結束。如果您調用flush(),並且每秒輸入一些文字的速度超過一個字母,則會將其輸入給您。例如,我輸入「你好」,然後停了下來,並得到了這樣的結果:

>>> flush() 
hello>>> 

在OS X的終端應用程序(至少),如果你的字text複製到剪貼板,調用函數,打糊在一秒內,這裏是你得到:

>>> flush() 
t>>> 

奇!只有第一個字母。再試一次,什麼也不輸入:

>>> flush() 
>>> 

它停頓了一秒鐘,什麼也不做,就像沒有輸入等待,對不對?再試一次,並創下?

>>> flush() 
ext?>>> 

你得到的糊的休息,保存起來,在?之前!另外,奇怪的是,在鍵入我不明白的?之前有1秒暫停。如果你在這一點再試一次,它就像正常一樣。

OK,讓我們再試一次,第一次粘貼text,然後將其粘貼WTF,然後鍵入!

>>> flush() 
t>>> flush() 
extW>>> flush() 
TF!>>> 

如此反覆粘貼只給出了第一個字母,並保持其他的輸入緩衝區,在W!之前暫停一秒。還有一件奇怪的事情:在Python >>>提示符處不輸入緩衝字符。

一個縈繞不定的問題:爲什麼在下一個字母被迴應之前,你還有額外的1秒暫停?選擇並不總是等待整個時間段...

回答

0

「無參數讀取」通常被引用爲讀取所有可用字節的方式,這對於此應用程序來說聽起來很完美。不幸的是,當你看到documentation時,read()和readall()是一樣的,它阻塞了EOF。所以你需要將stdin設置爲非阻塞模式。

一旦你這樣做,你開始:

IOError: [Errno 35] Resource temporarily unavailable 

當你google一下,絕大多數的答覆說,解決這個問題是要擺脫非阻塞模式...等等這在這裏沒有幫助。但是,this post解釋說,這就是非阻塞read()在沒有字符返回時所做的。

這裏是我的flush()功能,其中大部分來自後複製:

def flush(): 
    import sys, tty, termios, fcntl, os 
    fd = sys.stdin.fileno() 
    old_attr = termios.tcgetattr(fd) 
    old_fl = fcntl.fcntl(fd, fcntl.F_GETFL) 
    try: 
     tty.setraw(fd) 
     fcntl.fcntl(fd, fcntl.F_SETFL, old_fl | os.O_NONBLOCK) 
     inp = sys.stdin.read() 
    except IOError, ex1: #if no chars available generates exception 
     try: #need to catch correct exception 
      errno = ex1.args[0] #if args not sequence get TypeError 
      if errno == 35: 
       return '' #No characters available 
      else: 
       raise #re-raise exception ex1 
     except TypeError, ex2: #catch args[0] mismatch above 
      raise ex1 #ignore TypeError, re-raise exception ex1 
    finally: 
     termios.tcsetattr(fd, termios.TCSADRAIN, old_attr) 
     fcntl.fcntl(fd, fcntl.F_SETFL, old_fl) 
    return inp 

希望這是有幫助的人!