Как буферизируется std :: iostream?

Общий случай использования

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

Описание

Мне нужно прочитать пользовательский ввод, пока не будут нажаты некоторые разделители, чтобы можно было выполнить соответствующее действие. Этим разделителем может быть один «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 буферизуется, поэтому содержимое не записывается на диск, пока не произойдет какое-либо событие, но как насчет std :: istream. Это буферизовано? Если да, как это и как я могу обойти это поведение?

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

 Christian Garbin31 мар. 2012 г., 18:39
Вопрос очень хорошо описан, с ясной целью, обоснованием и примером кода. Хотелось, чтобы больше людей нашли время для этого.
 nikhil31 мар. 2012 г., 18:45
+! за очень хороший вопрос.

Ответы на вопрос(2)

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

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

Решение Вопроса

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

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

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

Канонический и неканонический ввод с терминала

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

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

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

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

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

 user16639031 мар. 2012 г., 18:51
+1 Это функция буферизации терминала - ничего не имеет прямого отношения к iostream.
 Bo Persson31 мар. 2012 г., 19:37
Да, это такочень зависит от платформы. Я работаю на мэйнфрейме с десятками тысяч подключенных терминалов. Это просто не может быть обеспокоено каждым нажатием клавиши на каждом терминале.

Ваш ответ на вопрос