Подтвердить что ты не робот

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

Общий прецедент

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

Описание

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

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

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

Зачем нужен встроенный разделитель вместо разделителя 'new-line'?

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

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

Что я до сих пор

До сих пор мой код выглядит следующим образом:

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. Буферизовано ли оно? Если да, то как это и каков мой вариант обойти это поведение?

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

Ответ 2

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

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