我的程序(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秒暫停?選擇並不總是等待整個時間段...