Как буферизируется std :: iostream? - PullRequest
21 голосов
/ 31 марта 2012

Общий вариант использования

Я пытаюсь реализовать базовую оболочку.

Описание

Мне нужно читать пользовательский ввод, пока не будут нажаты некоторые разделители, чтобы соответствующее действие моглобыть выполненным.Этим разделителем может быть один «a», один «b» или один «c».

Пример ввода может выглядеть следующим образом (где > - приглашение оболочки):

> 111-222-333-444a
Ok, '111-222-333-444' entered

Почему я хочу использовать встроенный разделитель вместо разделителя новой строки?

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

Поскольку я хотел бы прослушать событие клавиатуры, такое как «табулирование», для автоматического завершения текущей команды (реализация функции автозаполнения).

ЧтоДо сих пор у меня есть

. До сих пор мой код выглядит так:

bool done = false;
char c;
while (!done && std::cin.get(c))
{   
    switch (c)
    {
    case 'a':
        // Do something corresponding to 'a'
        done = true;
        break;

    case 'b':
        // Do something corresponding to 'b'
        done = true;
        break;

    case 'c':
        // Do something corresponding to 'c'
        done = true;
        break;

    default:
        // buffer input until a delimiter is pressed
        break;
    }
}

Однако цикл кажется выполненным только после нажатия клавиши «новая строка».Такое поведение убивает интерактивную сущность пользовательского ввода.

В чем вопрос?

Я знаю, что std :: ostream буферизуется, поэтому содержимое не записывается на диск, пока не произойдет какое-либо событие, но как насчетстанд :: IStream.Это буферизовано?Если да, как это и как я могу обойти это поведение?

Кроме того, я пометил этот вопрос как «домашнюю работу», потому что, если это не школьное упражнение, это упражнение, которое яЯ пытаюсь сделать это самостоятельно, и я не хочу выбирать только библиотеку, которая реализует все это.

Ответы [ 2 ]

7 голосов
/ 31 марта 2012

Если вы работаете в операционной системе POSIX, вы можете установить небуферизацию терминала, используя функции и структуры, объявленные в termios.h. В основном вам нужно отключить канонический ввод и настроить терминал на неканонический режим. Вот некоторые ссылки, которые могут помочь вам понять разницу между двумя режимами терминала:

  1. Неканонический ввод (из руководства libc)

    В неканоническом режиме ввода специальные символы редактирования, такие как ERASE и KILL, игнорируются. Системные средства для пользователя, чтобы редактировать ввод, отключены в неканоническом режиме, так что все вводимые символы (если они не являются специальными для сигналов или управления потоком) передаются прикладной программе точно так, как набрано. Прикладная программа должна предоставить пользователю способы редактирования ввода, если это необходимо.

  2. Канонический и неканонический терминальный вход

    Для канонического ввода - Think Shell; на самом деле, подумайте о старой доброй оболочке Bourne, так как Bash и ее родственники редактируют из командной строки. Вы вводите строку ввода; если вы допустили ошибку, вы используете символ стирания (по умолчанию это backspace, обычно; иногда DEL), чтобы стереть предыдущий символ ... Для неканонического ввода - подумайте vi или vim или ... you нажмите символ, и он сразу же доступен для программы. Тебя не задерживают, пока не нажмешь кнопку возврата.

  3. Описание интерфейса терминала

    В этой главе описывается общий интерфейс терминала, предназначенный для управления асинхронными коммуникационными портами. Он зависит от реализации, поддерживает ли он сетевые подключения или синхронные порты, или оба.

По сути, проблема, с которой вы сталкиваетесь, связана не с самим интерфейсом C ++ iostream, а скорее с тем, как настроен управляющий терминал, с которого читает интерфейс C ++ iostream. Таким образом, использование небуферизованного ввода-вывода будет зависеть от платформы и будет зависеть от того, используете ли вы Windows или реальную POSIX-совместимую платформу (включая POSIX-среды для Windows, такие как Cygwin).

Если вы обнаружите, что возиться с настройками терминала - слишком большая проблема, вы также можете заглянуть в кроссплатформенную библиотеку curses , такую ​​как PDCurses , которая будет абстрагироваться большинство сложностей базовых типов терминалов.

4 голосов
/ 31 марта 2012

Ответы на неотложные вопросы о том, буферизована ли std::istream и каким образом: да, std::istream является буфером, используя класс, полученный из std::streambuf, который определяет фактический подход к буферизации и чтению для конкретного источника (или, при использовании std::ostream для пункта назначения). Действительно ли это делает какую-либо буферизацию, зависит от этого конкретного класса, и его работу, как правило, нельзя избежать.

Тем не менее, это не ваша проблема! Проблема заключается в том, что обычно ввод не передается на стандартный ввод, если программа нажата до новой строки. Это сделано для того, чтобы некоторая правка строки могла быть реализована реализацией терминала и не должна выполняться каждой программой. К сожалению, нет портативного подхода, чтобы изменить это. В POSIX вы можете перевести стандартный поток ввода (используя дескриптор файла 0) в неканонический режим, используя tcgetattr() и tcsetattr(). Я не знаю, как этого добиться в системах без POSIX.

...