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)

Ответ 1

Похоже, вы делаете только фид строки, но возврат каретки. Измените печать на

print("ASD", end="\r\n")

Ответ 2

Google привел меня сюда, когда я искал ответ на этот же вопрос. Ключ, разделенный халексами без каретки, помог мне найти правду. Я нашел ответы в записи на Chris Wiki: 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()

Ответ 3

Проблема, с которой вы сталкиваетесь, - это разница между режимами "raw", "cooked" и "cbreak". И эти режимы являются режимами драйвера терминала уровня ядра, а не режимами вашего кода приложения или стандартной библиотеки или чего-либо еще в пользовательском пространстве.

В приготовленном режиме сам драйвер терминала имеет встроенные функции редактирования строк. Он обрабатывает обратное пространство, стирание слов (в основном, обратное целое слово одновременно) и подобные вещи. Ничего сложного, как использование клавиш со стрелками или истории или что-то в этом роде. Очень примитивный. В этом режиме ваша программа никогда не видит ничего от терминала до тех пор, пока не будет отправлен символ конца строки (eol), а затем ваша программа получит целую строку, а окончание строки будет переведено в стандарт Unix \n независимо от того, что терминал фактически делает. Кроме того, как часть этого, драйвер терминала повторяет введенные символы обратно на терминал, чтобы пользователь мог видеть, что они набирают.

В режиме "приготовленного" драйвер терминала уровня ядра также выполняет некоторый перевод вывода. И часть этого превращает \n в \r\n, если это необходимо.

Кроме того, в режиме "приготовленного" драйвер терминала обрабатывает специальные символы, такие как Control-C (отправляет SIGINT в группу процессов управления (транслируется CPython в исключение KeyboardInterrupt)) и Control-Z (отправляет SIGTSTP (например, SIGSTOP, но можно поймать) в группу процессов управления).

В режиме 'cbreak' редактирование строк больше не выполняется. Драйвер терминала дает каждому персонажу (или короткую последовательность символов, такую ​​как escape-последовательность для клавиши со стрелкой), в программу немедленно. Эти символы не отображаются на экране, поэтому, если ваша программа не распечатает их, пользователь их не увидит. Драйвер терминала хотя и обрабатывает специальные символы, такие как Control-C и Control-Z, хотя он перестает обрабатывать символы редактирования строк, такие как backspace или символ стирания слов (обычно Control-W). Кроме того, некоторая обработка вывода все еще выполняется, поэтому драйвер превращает \n в \r\n.

В режиме "raw" обработка не выполняется ни на входе, ни на выходе. Никакой специальной обработки символов, без эха, без преобразования \n в \r\n, без обработки для Control-Z ничего. Это до программы, которая помещает терминал в необработанном режиме, чтобы сделать все это.

Теперь вы устанавливаете атрибуты для sys.stdin, чтобы вы могли подумать, что это не должно влиять на sys.stdout. Но, на самом деле, оба дескриптора файла приводят к тому же "экземпляру" драйвера терминала. И это настройки для драйвера терминала, которые определяют, что происходит. Поэтому неважно, измените ли вы эти параметры с помощью sys.stdin, sys.stdout или даже sys.stderr, все измените один и тот же базовый экземпляр драйвера терминала, и они повлияют на все остальные.

Это, конечно, не относится к файловым дескрипторам, которые были перенаправлены оболочкой перед запуском вашей программы.