Вопрос по linux, stdin, python, buffering – Python в режиме raw stdin print добавляет пробелы
Мне нужно было переключить стандартный ввод в небуферизованный режим в Python, чтобы я мог читать с него отдельные символы. Мне удалось заставить его работать, но теперь стандартный вывод не работает: как-то похоже на то, что после символа новой строки выдается несколько пробелов, ноль в первой строке, 3 во второй, 6 на третьей и т. Д. :
ASD
ASD
ASD
Операционная система - Ubuntu Linux 12.04, 64-разрядная версия, версия Python - 3.2.3.
Как я могу избавиться от этого поведения?
Ниже приведен код, который я использовал:
import sys
import tty
import termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
tty.setraw(sys.stdin)
for i in range(0, 10):
print("ASD")
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
Похоже, вы только переводите строки, но не возвращаете карету. Измените свою печать на
print("ASD", end="\r\n")
\r
вручную под Linux. Спасибо
K.Steff
Проблема, с которой вы сталкиваетесь, заключается в разнице между «необработанными», «приготовленными» и «cbreak»; режимы. И эти режимы являются режимами драйвера терминала уровня ядра, а не режимами кода вашего приложения или стандартной библиотеки или чего-либо еще в пользовательском пространстве. Это старый способ Unix сослаться на них. Posix заменил их гораздо более детализированным набором атрибутов, хотя атрибуты Posix обычно переворачиваются вместе с вспомогательными функциями таким образом, что имитирует старые «raw», «cooked». и «cbreak»; режимы.
В готовом режиме драйвер терминала имеет встроенную функцию редактирования примитивной строки. Он обрабатывает возврат, удаление слова (в основном возврат всего слова сразу) и тому подобные вещи. Ничего сложного, например, обработка клавиш со стрелками, история или что-то подобное. Очень примитивно. В этом режиме ваша программа никогда не видит ничего из терминала до тех пор, пока не будет отправлен символ конца строки (eol), а затем ваша программа получит целую строку, а окончание строки переведено в стандарт Unix.\n
независимо от того, что на самом деле делает терминал. Кроме того, как часть этого, драйвер терминала эхолотит введенные символы обратно в терминал, чтобы пользователь мог видеть, что он печатает.
В «приготовленные» В этом режиме драйвер терминала уровня ядра также выполняет некоторую трансляцию вывода. И часть этого превращается\n
в\r\n
если нужно.
Кроме того, в «приготовленные» режим, в котором драйвер терминала обрабатывает специальные символы, такие как Control-C (отправляет SIGINT в контролирующую группу процессов (переведенная CPython в исключение KeyboardInterrupt)) и Control-Z (отправляет SIGTSTP (как SIGSTOP, но может быть перехвачен) в группа контролирующих процессов).
In 'cbreak' В режиме редактирования строки больше нет. Драйвер терминала передает каждый символ (или короткую последовательность символов, например, escape-последовательность для клавиши со стрелкой) программе немедленно. Эти символы не отображаются на экране, и поэтому, если ваша программа не напечатает их, пользователь их не увидит. Драйвер терминала, тем не менее, обрабатывает специальные символы, такие как Control-C и Control-Z, хотя он перестает обрабатывать символы редактирования строки, такие как backspace или символ удаления слова (обычно Control-W). Кроме того, некоторая обработка вывода еще выполняется, поэтому драйвер\n
в\r\n
.
In 'raw' В этом режиме обработка не выполняется ни на входе, ни на выходе. Никакой специальной обработки символов, никакого эха, никакого преобразования\n
в\r\n
, нет обработки для Control-Z, ничего. Все зависит от программы, которая переводит терминал в необработанный режим.
Теперь вы устанавливаете атрибуты дляsys.stdin
так что вы можете подумать, что это не должно влиятьsys.stdout
, Но на самом деле оба ваших файловых дескриптора приводят к одному и тому же «экземпляру»; терминального драйвера. И именно настройки драйвера терминала определяют, что происходит. Так что не имеет значения, если вы измените эти настройки черезsys.stdin
, sys.stdout
, или дажеsys.stderr
все они изменяют один и тот же базовый экземпляр драйвера терминала и влияют на все остальные.
Это, конечно, не относится к дескрипторам файлов, которые были перенаправлены оболочкой до запуска вашей программы.
В качестве примечания вы можете использоватьstty -a
в командной строке, чтобы увидеть полное чтение всех этих флагов (включая то, какие управляющие символы приводят к тому, какие сигналы в режимах приготовления и разбивания).
Google привел меня сюда, когда я искал ответ на этот же вопрос. Подсказка, которую поделил Халекс «Возврат кареты», помогла мне найти правду. Я нашел свои ответы в посте Криса Вики:https://utcc.utoronto.ca/~cks/space/blog/unix/CBreakAndRaw что привело меня к чтению источника tty.py здесь:https://hg.python.org/cpython/file/618ea5612e83/Lib/tty.py Который привел меня к выводу, что если целью является чтение отдельных символов, а не:
tty.setraw()
Использование:
tty.setcbreak()