С очистить буфер клавиатуры
Программа должна очистить буфер клавиатуры, перед тем, как
выдать запрос на ввод, исключая тем самым посторонние нажатия
клавиш, которые могут к тому времени накопиться в буфере. Буфер
может накапливать до 15 нажатий на клавишу, независимо от того,
являются ли они однобайтными кодами ASCII или двухбайтными расши-
ренными кодами. Таким образом, буфер должен отвести два байта
памяти для каждого нажатия на клавишу. Для однобайтных кодов
первый байт содержит код ASCII, а второй - скан-код клавиши. Для
расширенных кодов первый байт содержит ASCII 0, а второй номер
расширенного кода. Этот код обычно совпадает со скан-кодом клави-
ши, но не всегда, поскольку некоторые клавиши могут комбиниро-
ваться с клавишами сдвига для генерации различных кодов.
Буфер устроен как циклическая очередь, которую называют также
буфером FIFO (первый вошел - первый ушел). Как и любой буфер он
занимает непрерывную область адресов памяти. Однако не имеется
определенной ячейки памяти, которая хранит "начало строки" в
буфере. Вместо этого два указателя хранят позиции головы и хвоста
строки символов, находящейся в буфере в текущий момент. Новые
нажатия клавиш запасаются в позициях, следующих за хвостом (в
более старших адресах памяти) и соответственно обновляется указа-
тель хвоста буфера. После того, как израсходовано все буферное
пространство, новые символы продолжают вставляться, начиная с
самого начала буферной области; поэтому возможны ситуации, когда
голова строки в буфере имеет больший адрес, чем хвост. После того
как буфер заполнен, новые вводимые символы игнорируются, при этом
прерывание клавиатуры выдает гудок через динамик. На рис. 3-2
показаны некоторые возможные конфигурации данных в буфере.
В то время как указатель на голову установлен на первый вве-
денный символ, указатель на хвост установлен на позицию за пос-
ледним введенным символом. Когда оба указателя равны, то буфер
пуст. Чтобы разрешить ввод 15 символов требуется 16-я пустая
позиция, 2 байта которой всегда содержат код возврата каретки
(ASCII 13) и скан-код клавиши <Enter>, равный 28. Эта пустая
позиция непосредственно предшествует голове строки символов. 32
байта буфера начинаются с адреса 0040:001E. Указатели на голову и
хвост расположены по адресам 0040:001A и 0040:001C, соответствен-
но. Хотя под указатели отведено 2 байта, используется только
младший байт. Значения указателей меняются от 30 до 60, что соот-
ветствует позициям в области данных BIOS. Для очистки буфера надо
просто установить значение ячейки 0040:001A равным значению ячей-
ки 0040:001C.
Отметим, что программа имеет возможность вставлять символы в
буфер, завершая строку символом возврата каретки и соответственно
меняя значения указателей. Если это проделать правильным образом
перед завершением программы, то при возврате управления в MS DOS
эти символы будут считаны и может быть автоматически загружена
другая программа.
Низкий уровень.
В Бейсике для получения и изменения значений указателей буфера
используются операторы PEEK и POKE:
Этот метод не самый лучший. Некоторые программы могут создавать
буфер где-нибудь в другом месте памяти, а кроме того, всегда
существует возможность, что посреди строки 110 произойдет преры-
вание клавиатуры, которое изменит указатель хвоста. По этим при-
чинам лучше оставить указатели буфера в покое. Вместо этого,
лучше читать из буфера до тех пор, пока не будет возвращен символ
ASCII 0, показывающий, что буфер пуст:
Средний уровень.
Функция 0C прерывания 21H выполняет любую из функций ввода с
клавиатуры 1, 6, 7, 8 и A (описанных в этой главе), но перед этим
чистит буфер клавиатуры. Надо просто поместить номер функции
ввода в AL (в этом примере - 1):
;---очистка буфера перед ожиданием нажатия клавиши
MOV AH,0CH ;выбираем функцию DOS 0CH
MOV AL,1 ;выбираем функцию ввода символа
INT 21H ;чистим буфер, ждем ввода
Низкий уровень.
Как и в примере высокого уровня делаем значение указателя на
хвост равным значению указателя на голову. Для избежания влияния
прерывания клавиатуры запрещаем прерывания на время модификации
указателя:
;---выравниваем значения указателей на голову и хвост
CLI ;запрещаем прерывания
SUB AX,AX ;обнуляем регистр
MOV ES,AX ;добавочный сегмент - с начала памяти
MOV AL,ES:[41AH] ;берем указатель на голову буфера
MOV ES:[41CH],AL ;посылаем его в указатель хвоста
STI ;разрешаем прерывания
Те, кто использовал C ++, понимают, что случайные ошибки ввода будут возникать в процессе использования ввода, что приведет к тому, что последующий ввод будет недействительным. C ++ продолжит использовать предыдущий случайный ввод, что приведет к неправильному запуску программы.
Когда ввод с клавиатуры заканчивается, входные данные сохраняются во входном буфере, и cin Функция считывает данные прямо из входного буфера. Этот буферный механизм предусматривает, что только после получения клавиши Enter все входные данные будут отправлены в cin функция. Возврат каретки отмечает завершение одного ввода. Если данных недостаточно, он будет ждать, пока пользователь продолжит ввод; если данные избыточны, избыточные данные будут сохранены в буфере входного потока для следующего использования.
1.cin.clear()
Это значение каждого идентификатора статуса:
(перевод youdao)
- goodbit не имеет ошибок, cin.rdstate () равно 0
- eofbit достиг конца входного файла, cin.rdstate () равно 1
- Логическая ошибка операции ввода-вывода failbit (нефатальная ошибка ввода / вывода, устранимая), cin.rdstate () равно 2
- Ошибка чтения / записи Badbit операции ввода-вывода (фатальная ошибка ввода-вывода, необратимая), cin.rdstate () равно 4
Например: определите, что вводимая переменная является целым числом, но если мы введем другие символы, произойдет ошибка. cin Есть способ обнаружить эту ошибку: cin.rdstate() ; когда cin.rdstate() Если он возвращает 0 (т.е. ios :: goodbit), это означает, что ошибки нет, и вы можете продолжить вход или работу. Если он возвращает 2, возникает нефатальная ошибка (например, ios :: failbit), и вы не можете продолжить войти или работать.
в заключении:
cin.clear () может сделать биты состояния нормальными, но не может очистить входной буфер.
2.cin.sync()
но! ! !
Грубо говоря: для входных потоков стандартных библиотек, таких как std :: cin, вызов sync () является поведением, «определяемым реализацией». Если ожидаемый эффект не достигается, вы можете проверить свой собственный компилятор (реализация стандартной библиотеки) документации, должно быть описание того, какое поведение он использует.
Ниже приведены некоторые бесполезные проверки. Если вам интересно, ознакомьтесь с моим процессом проверки .
Из любопытства я открыл инструмент командной строки VS2019 (Инструменты-Командная строка-Инструмент командной строки разработчика, ввод RC/? )
Но я искал официальный сайт и GitHub и не нашел документации по компилятору . Но, по крайней мере, я знаю, что Microsoft C ++ IDE использует компилятор MSVC.
Просто посмотрите определение функции синхронизации? <istream> В исходном документе функция синхронизации определяется следующим образом:
(В сочетании с китайским документом для пояснения)
Синхронизируйте входной буфер со связанным источником данных. Ведет себя как неформатированная функция ввода (UnformattedInputFunction), за исключением того, что она не влияет на gcount () для создания и проверки сторожевого объекта. Если rdbuf () является нулевым указателем, он возвращает -1; в противном случае он вызывает rdbuf () -> pubsync (). Если функция возвращает -1, вызовите setstate (badbit) и верните -1, в противном случае она вернет 0.
Но обратите внимание на это предложение в китайском документе:
Для того, чтобы следующая операция чтения зафиксировала любые изменения, которые могли быть внесены в связанную входную последовательность после того, как буфер потока окончательно заполнил свою область сбора данных. Для этого sync () может очистить область сбора данных, или заполнить ее, или ничего не делать. Заметным исключением является Visual Studio, где эта операция сбрасывает необработанный вывод при вызове со стандартным потоком ввода.
Почему MSVC, который я использовал, является исключением, он не ароматный!
Посмотрите на код:
в заключении
В разных компиляторах функция синхронизации реализована по-разному, что делает ее функции разными.Я использую MSVC, который не может выполнять функцию очистки буфера.
3.cin.ignore()
numeric_limits<std::streamsize>::max() Но это climits Максимальное значение, используемое потоком, определенное файлом заголовка, вы также можете заменить его на достаточно большое целое число.
Посмотрите на код:
в заключении:
Чтобы сделать буфер полностью пустым, самое безопасное решение:
Как объяснил автор приведенного выше кода: Программа не будет работать должным образом, потому что в строке 1, когда пользователь нажимает Enter, во входном буфере остается 2 символа: Enter key (ASCII code 13) и \n (ASCII code 10) . Следовательно, в строке 2 он будет читать \n и не будет ждать, пока пользователь введет символ.
Хорошо, я понял. Но мой первый вопрос: почему второй getchar() ( ch2 = getchar(); ) не читает Enter key (13) , а не \n символ?
Далее автор предложил 2 способа решения таких проблем:
напишите такую функцию:
Этот код действительно работал. Но я не могу себе объяснить, как это работает? Поскольку в операторе while, который мы используем getchar() != '\n' , это означает чтение любого отдельного символа, кроме '\n' ? если да, то во входном буфере все равно остается '\n' символ?
Программа не будет работать должным образом, потому что в строке 1, когда пользователь нажимает Enter, он оставит в буфере ввода 2 символа: клавишу Enter (код ASCII 13) и \ n (код ASCII 10). Следовательно, в строке 2 он будет читать \ n и не будет ждать, пока пользователь введет символ.
Поведение, которое вы видите в строке 2, правильное, но это не совсем правильное объяснение. В потоках текстового режима не имеет значения, какие окончания строки использует ваша платформа (будь то возврат каретки (0x0D) + перевод строки (0x0A), чистый CR или простой LF). Библиотека времени выполнения C позаботится об этом за вас: ваша программа будет видеть только '\n' символы новой строки.
Если вы набрали символ и нажали Enter, то этот вводимый символ будет прочитан в строке 1, а затем '\n' будет прочитан в строке 2. См. Я использую, scanf %c чтобы прочитать ответ Y / N, но более поздний ввод пропускается.из FAQ по comp.lang.c.
Что касается предлагаемых решений, см. (Опять же из FAQ comp.lang.c):
которые в основном заявляют, что единственный переносимый подход - это сделать:
Твой getchar() != '\n' цикл работает, потому что после вызова getchar() возвращенный символ уже был удален из входного потока.
Кроме того, я чувствую себя обязанным отговорить вас от употребления scanf полностью: почему все говорят не использовать scanf ? Что мне использовать вместо этого?
Как ПОЛНОСТЬЮ очистить буффер клавиатуры.
while keypressed do readkey
Программа должна очистить буфер клавиатуры, перед тем, как выдать запрос на ввод, исключая тем самым посторонние нажатия клавиш, которые могут к тому времени накопиться в буфере. Буфер может накапливать до 15 нажатий на клавишу, независимо от того, являются ли они однобайтными кодами ASCII или двухбайтными расширенными кодами.
Таким образом, буфер должен отвести два байта памяти для каждого нажатия на клавишу. Для однобайтных кодов первый байт содержит код ASCII, а второй - скан-код клавиши. Для расширенных кодов первый байт содержит ASCII 0, а второй номер расширенного кода. Этот код обычно совпадает со скан-кодом клавиши, но не всегда, поскольку некоторые клавиши могут комбинироваться с клавишами сдвига для генерации различных кодов.
Буфер устроен как циклическая очередь, которую называют также буфером FIFO (первый вошел - первый ушел). Как и любой буфер он занимает непрерывную область адресов памяти. Однако не имеется определенной ячейки памяти, которая хранит "начало строки" в буфере. Вместо этого два указателя хранят позиции головы и хвоста строки символов, находящейся в буфере в текущий момент. Новые нажатия клавиш запасаются в позициях, следующих за хвостом (в более старших адресах памяти) и соответственно обновляется указатель хвоста буфера. После того, как израсходовано все буферное пространство, новые символы продолжают вставляться, начиная с самого начала буферной области; поэтому возможны ситуации, когда голова строки в буфере имеет больший адрес, чем хвост. После того как буфер заполнен, новые вводимые символы игнорируются, при этом прерывание клавиатуры выдает гудок через динамик.
В то время как указатель на голову установлен на первый введенный символ, указатель на хвост установлен на позицию за последним введенным символом. Когда оба указателя равны, то буфер пуст. Чтобы разрешить ввод 15 символов требуется 16-я пустая позиция, 2 байта которой всегда содержат код возврата каретки (ASCII 13) и скан-код клавиши <Enter>, равный 28. Эта пустая позиция непосредственно предшествует голове строки символов. 32 байта буфера начинаются с адреса 0040:001E. Указатели на голову и хвост расположены по адресам 0040:001A и 0040:001C, соответственно. Хотя под указатели отведено 2 байта, используется только младший байт. Значения указателей меняются от 30 до 60, что соответствует позициям в области данных BIOS. Для очистки буфера надо просто установить значение ячейки 0040:001A равным значению ячейки 0040:001C.
Отметим, что программа имеет возможность вставлять символы в буфер, завершая строку символом возврата каретки и соответственно меняя значения указателей. Если это проделать правильным образом перед завершением программы, то при возврате управления в MS DOS эти символы будут считаны и может быть автоматически загружена другая программа.
Средний уровень
Функция 0C прерывания 21H выполняет любую из функций ввода с Клавиатуры, но перед этим чистит буфер клавиатуры. Надо просто поместить номер функции ввода в AL (в этом примере - 1):
Низкий уровень
Как и в примере высокого уровня делаем значение указателя на хвост равным значению указателя на голову. Для избежания влияния прерывания клавиатуры запрещаем прерывания на время модификации указателя:
Читайте также: