Текстовый ввод-вывод

Ввод с клавиатуры

Разбить на страницы
Показывать лекцию целиком

Прямой ввод-вывод "на среднем уровне" позволяет вводить и выводить текстовые символы в файл, на консоль, модем, удалённый терминал. При этом в MS-DOS он организован при помощи механизма прерываний. Для иллюстрации "прямого ввода" будут приведены функции прямого ввода с консоли на языках Ассемблер и Си.

5.1. Введение. Общие сведения о клавиатуре

Клавиатура - это не просто "доска с клавишами" (дословный перевод английского названия "keyboard"). Помимо механической части (собственно клавиш и контактов) она включает в себя сложную топологию проводников, и даже собственный микропроцессор - контроллер.

Задачи, решаемые этим микропроцессором, следующие:

  • Реагировать на нажатие клавиш или комбинации клавиш;
  • Реагировать на "длительное" нажатие на клавишу, и осуществлять соответствующие действия;
  • По положению клавиш генерировать специальный скен-код клавиши (см. раздел 5.1.1.);
  • В соответствии с "таблицей символов" (codepage) преобразовывать скен-код клавиши в ASCII символ, ей соответствующей;

    Примечание: некоторые "серые" (управляющие) клавиши не генерируют символы, а используются только для управления компьютером. В этом случае для них ASCII символ не генерируется (записывается в 0).

  • Заносит пару "ASCII код" - "Скен код" в клавиатурный буфер.
  • Видите, как всё сложно.… Теперь разберёмся с остальным.

    5.1.1. Понятие о скэн-кодах клавиш

    При нажатии клавиши на клавиатуре генерируется её скен-код. Скен-код можно считать "номером клавиши" на клавиатуре. Однако это не совсем так. Например, если скен код клавиши "F10" равен "68", то тот же код клавиши с нажатой клавишей "Shift" будет равен "93", с клавишей "Ctrl" - "103", а с клавишей "Alt" - "113".

    Существуют так называемые "алфавитно-цифровые" ("белые") и "управляющие" ("серые") клавиши. Первые генерируют скен-код и ASCII символы. Вторые генерируют только расширенный скен-код, а в поле "ASCII символа" стоит ноль. Благодаря этому программист легко может понять, как обрабатывать код клавиши: выводить ли символ на экран или запускать управляющую последовательность действий.

    Более подробно о скэн-кодах клавиш смотри [1, 8, 33].

    5.1.2. "Эхопечать" символов

    При вводе с клавиатуры пара значений "Скен-код" - "ASCII символ" заносится в клавиатурный буфер для её обработки на компьютере. При этом на старых терминалах все введённые символы отображались на экране дисплея. Но, с появлением персональных ЭВМ, выяснилось, что такой вывод ("Эхопечать") не всегда удобный. Что делать, если Вы вводите пароль, и Вы не хотите, чтобы кто-либо его прочитал с экрана? А что делать, если Вы используете алфавитно-цифровую клавишу как управляющую (например, при обработке меню)? В этом случае Вам помогут функции ввода с клавиатуры без эхопечати.

    5.1.3. Клавиатурный буфер

    Полученный в результате преобразований в контроллере двухбайтовый код посылается в кольцевой буфер ввода, который служит для синхронизации ввода данных с клавиатуры и приёма их выполняемой программой. Объём кольцевого буфера составляет 15 слов (30 байт). При этом буфер организован по принципу: "первым записан - первым считан" (английская аббревиатура "FIFO"). При переполнении буфера новые коды в него не поступают, а нажатие на клавиши вызывает предупреждающие сигналы.

    5.2. Основные функции для ввода символов с клавиатуры

    5.2.1. Консольный ввод в Ассемблере

    5.2.1.1. Ожидание ввода символа без эхопечати

    Функции 7 и 8 прерывания 21H ожидают ввода символа, если буфер клавиатуры пуст; появление символа на экране не отображается.

    Разница между этими функциями в том, что функция 8 распознаёт специальное сочетание клавиш "Ctrl+Break", а функция 7 игнорирует это сочетание клавиш.

    В обоих случаях функция возвращает символ в регистр AL. Если AL содержит ASCII 0, то получен расширенный код. Повторите вызов прерывания с теми же параметрами, и в AL появится второй байт расширенного кода.

    Пример 5.1

    ;--- получаем введённый символ
    	MOV AH,8	;номер функции
    	INT 21H
    	CMP AL,0		;проверяем на расширенный код
    	JE EXT_COD	;если да, то переход на анализ кода
    	…		; иначе - обработка символа
    ;--- анализ расширенного кода
    EXT_COD:	INT 21H	; берём второй байт кода
    	CMP AL, 75	; проверяем на "стрелка влево"
    	JE CUR_LEFT	; переход к обработчику
    	CMP AL, 77	; проверяем на "стрелка вправо"
    	JE CUR_RIGHT	; переход к обработчику

    5.2.1.2. Ожидание ввода символа с эхопечатью

    При вводе символов с эхопечатью эхо вводимых символов выдаётся на экран. При этом символы "возврат каретки" и "забой" переводятся в соответствующие перемещения курсора, а не отображаются как ASCII символы. Вывод эха осуществляется с текущей позиции на экране. При этом курсор после вывода смещается на позицию вправо, а по достижении правой границы экрана - переносит вывод на новую строку.

    Функция 1 прерывания 21H ожидает ввода символа с клавиатуры, если её буфер пуст, а затем выводит его на экран в текущую позицию курсора. Функция обрабатывает нажатие "Ctrl+Break". Введённый символ сохраняется в регистре AL. Если содержимое AL равно ASCII 0, то для получения расширенного кода повторите прерывание.

    Пример использования этой функции аналогичен примеру 5.1.

    Функция 1 прерывания 21H полностью игнорирует клавишу "Esc". Клавиши табуляции интерпретируются нормально. Клавиша "Backspace" сдвигает курсор на одну позицию влево, но не стирает символ в этой позиции. Клавиша "Enter" вызывает перемещение курсора на первую позицию строки (нет автоматического переноса на новую строку).

    5.2.1.3. Получение строки символов

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

    Функция 0AH прерывания 21H позволяет вводить строку длиной до 254 символов, выдавая эхо на терминал. Эта функция продолжает ввод символов до тех пор, пока не нажата клавиша "возврат каретки" ( "Enter" ). DS:DX указывает на строку, куда помещаются вводимые символы. При вводе первый байт этой позиции должен содержать число байтов, отводимой для этой строки. После того, как строка введена, второй байт даёт число реально введённых символов. Сама строка начинается с третьего байта.

    Надо отвести достаточно памяти для строки нужной длины, плюс 2 байта для дескриптора строки плюс 1 добавочный байт для символа "Возврат каретки". Код возврата каретки: ASCII 13, - вводится как последний символ строки, но не учитывается в результате, который функция помещает во второй байт дескриптора.

    Таким образом, для получения 50 - символьной строки надо отвести минимум 53 байта памяти, и поместить в первый байт памяти строки число ASCII 51. После ввода 50 символов второй байт дескриптора будет содержать ASCII 50, а 53-й байт отведённой памяти будет содержать ASCII 13.

    Пример 5.2.

    ; - - - в сегменте данных
    STRING	DB	53DUP(?) ; область для 50 символов
    ; - - - получение строки с клавиатуры
    	LEA DX, STRING	; DS:DX - адрес строки
    	MOV BX,DX
    	MOV AL,51	; установка длины строки (+1 для CR)
    	MOV [BX], AL	; посылаем в первый байт дескриптора
    	MOV AH, 0AH	; номер функции
    	INT 21H
    ; - - - получаем в AH длину строки
    	MOV AH, [BX]+1	; теперь длина в AH

    В этой процедуре можно использовать возможности редактирования строки MS-DOS. Нажатие "Backspace" или "стрелка влево" удаляет символ с экрана без помещения его в память. Работает клавиша табуляции, а расширенные коды игнорируются. Допускаются пустые строки (содержащие только символ ASCII 13). На дисплее при достижении правого края экрана вводимая строка переносится на следующую строку дисплея, а при достижении правого нижнего угла экран сдвигается на строку вверх. Если вводится больше символов, чем отведено для строки, лишние символы игнорируются, и включается гудок динамика.

    5.2.2. Консольный ввод в Си

    В языке Си и его разновидностях существуют следующие функции для ввода данных с клавиатуры:

  • cgets - чтение строки с консоли;
  • getch - чтение символа с консоли;
  • getche - чтение символа с эхопечатью;
  • kbhit - проверка нажатия клавиши на консоли;
  • cscanf - чтение данных с консоли по формату;
  • getpass - ввод с терминала пароля без эхопечати.
  • Рассмотрим некоторые из них поподробнее

    ФУНКЦИЯ GETCH
    ОПИСАНА В: <conio.h> /* Используется только для описания функции */
    СИНТАКСИС: int getch() ;
    НАЗНАЧЕНИЕ: Ввод одиночного символа с консольного терминала без эхопечати (без вывода на дисплей).
    ФУНКЦИЯ ВОЗВРАЩАЕТ: прочитанный символ.
    ПРИМЕЧАНИЕ: функция getch использует входной поток stdin.

    ФУНКЦИЯ GETCHE
    ОПИСАНА В: <conio.h> /* Используется только для описания функции */
    СИНТАКСИС: int getche();
    НАЗНАЧЕНИЕ: Функция читает одиночный символ с консольного терминала, с эхопечатью введённого символа на экране с текущей позиции курсора.
    ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: Прочитанный символ.
    ПРИМЕЧАНИЕ:
  • Нет ошибочных кодов возврата;
  • Функция корректно обрабатывает "Ctrl+Break".
  • ФУНКЦИЯ CGETS
    ОПИСАНА В: <conio.h> /* Используется только для описания функции */
    СИНТАКСИС:
    char *cgets(str);
    char *str;
    НАЗНАЧЕНИЕ: Функция читает строку символов непосредственно с консоли, и помещает строку и её длину по указателю str. Аргумент str должен быть указателем на массив символов;
    ЗАМЕЧАНИЕ: Первый элемент массива str[0] должен содержать максимально допустимую длину считываемой строки. Массив должен иметь достаточную длину, чтобы поместить строку, символ ASCII 0 плюс два байта дополнительно для дескриптора;

    cgets читает символы до тех пор, пока не прочитаны символы <CR> и <LF>, или пока не будет прочитано установленное число символов. Прочитанная строка начинается со str[2]. Если прочитана комбинация <CR>/<LF>, эти символы будут располагаться за символом конца строки (ASCII 0). Действительная длина строки помещается в str[1] ;

    ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: указатель на начало строки, то есть на адрес элемента str[2] ;
    ПРИМЕЧАНИЕ:
  • Нет кодов ошибок;
  • Длина строки ограничена ~ 250 символами.
  • В примере 5.3 иллюстрирована работа функции cgets.

    Пример 5.3

    /* Пример ex05003.c */
    /* Определение констант - длины буфера */
    #define LETTER_SIZE	50 // Максимальное число символов
    #define BUFF_SIZE		55 // Общий размер буфера = LETTER_SIZE+5
    /* Объявление переменной буфера */
    char *buff[BUFF_SIZE];
    char csize; //служебная переменна
    /* Подготовительные операции и чтение с клавиатуры в буфер */
    csize = BUFF_SIZE - 2; // Истинный размер буфера в символах
    buff[0] = csize;	// Заносим это значение в буфер;
    (void) cgets( buff );	// Читаем с клавиатуры в буфер
    if( buff[1] > LETTER_SIZE ) goto ERROR; // Если переполнение буфера, следуем за ссылкой ERROR.

    Благодаря механизму дескрипторов (двух первых "служебных" байтов строки-буфера) "переполнения буфера" достичь действительно сложно. MS-DOS просто не допустит чтения большего числа символов, чем это указано в байте buff[0].

    ФУНКЦИЯ CSCANF
    ОПИСАНА В: <conio.h>
    СИНТАКСИС:
    int cscanf( format {, argument} );
    char *format;
    НАЗНАЧЕНИЕ: Функция cscanf читает данные с консоли, выполняет форматные преобразования и помещает значения в переменные, адреса которых определяются аргументами функции.
    ЗАМЕЧАНИЯ:
  • Функция cscanf использует функцию getche для ввода символов;
  • Каждый аргумент должен указывать на переменную того типа, который соответствует её описанию в строке: "format". См. также описание функции scanf, в приложении №II (6.7) к лекции 6.
  • ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ:
  • количество элементов ввода, которые были успешно преобразованы и присвоены;
  • значение EOF, если встретился символ - признак конца файла;
  • 0, если нет присвоенных областей.
  • Пример: 5.4.

    int i; // Целая переменна
    /* Строка формата для ввода целого числа, состоящего из 10 знаков */
    const char format[] = "%10d";
    int ierr; // переменная ошибки ввода
    /* Ввод значения с консоли */
    ierr = cscanf( format, i );
    if( ierr != 1 )goto ERROR; // переход, если ошибка ввода
    ФУНКЦИЯ GETPASS
    ОПИСАНА В: <conio.h>
    СИНТАКСИС:
    char *getpass( prompt );
    char *prompt;
    НАЗНАЧЕНИЕ: Функция getpass используется для ввода с консольного терминала пароля, который оператор должен набрать в ответ на выводимое на экран приглашение, задаваемое строкой prompt с завершающим символом "\0". При вводе пароля отключается эхопечать вводимых с терминала символов.
    ЗАМЕЧАНИЕ: длина пароля составляет не более 8 символов.
    ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: указатель на строку с завершающим нулём, содержащую не более восьми первых символов текста.

    ПРИМЕЧАНИЕ: Пароль хранится в статическом буфере (символьном массиве) функции и переписывается при каждом обращении к ней.

    5.2.3. Консольный ввод в Quick Basic

    Для ввода символов в Quick Basic используются одна функция и один оператор:

  • функция INKEY$ - для ввода символов без эхопечати;
  • оператор INPUT - для ввода значений, в том числе и форматированный ввод.
  • Рассмотрим эти функции поподробнее

    ФУНКЦИЯ INKEY$
    СИНТАКСИС: INKEY$
    НАЗНАЧЕНИЕ: Функция для ввода символов с клавиатуры без эхопечати.
    ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ:
  • пустая строка - символ не введён;
  • один байт с ASCII - символом - нажата алфавитно-цифровая клавиша;
  • два байта - нажата "серая" клавиша.
  • При этом левый байт будет всегда нулевой, а правый содержит расширенный скен ход клавиши.

    Пример 5.5

    REM ФАЙЛ: "EX05005.BAS"
    REM ПРИМЕР ИСПОЛЬЗОВАНИЯ ФУНКЦИИ INKEY$
    REM ОЖИДАЕМ НАЖАТИЯ КЛАВИШИ
    100 C$ = INKEY$
    IF C$ = "" GOTO 100
    REM ПРОВЕРЯЕМ, НАЖАТА ЛИ "СЕРАЯ" КЛАВИША
    IF LEN( C$ ) = 2 GOSUB 2000
    …
    REM "КОНСТРУКЦИЯ" ДЛЯ ОБРАБОТКИ СКЕН КОДОВ
    2000 C$ = RIGHT( C$, 1 )
    REM ПРЕОБРАЗУЕМ КОД В ЧИСЛО
    C = ASC( C$)
    REM НАЖАТА КЛАВИША "СТРЕЛКА ВЛЕВО"
    IF C = 75 THEN GOSUB 3000
    REM НАЖАТА КЛАВИША "СТРЕЛКА ВПРАВО"
    IF C = 77 THEN GOSUB 3100
    …
    ОПЕРАТОР INPUT
    СИНТАКСИС: INPUT аргумент
    НАЗНАЧЕНИЕ: Оператор для форматированного ввода с клавиатуры с эхопечатью. Оператор автоматически переводит текст, набранный на клавиатуре, в формат, соответствующий формату вводимой переменной. Поддерживаются форматы:
  • вещественных чисел;
  • целых чисел;
  • символьных строк.
  • ЗАМЕЧАНИЯ:
  • Ввод символов завершается нажатием клавиши "Enter" ; код ASCII 13 не добавляется в конец строки;
  • До нажатия этой клавиши текст можно редактировать средствами редактирования строки DOS;
  • Оператор "не воспринимает" "серые клавиши" на клавиатуре. Реакция на такое нажатие зависит от реализации Бейсика.
  • ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: Нет.

    Приведём пример ввода строки символов с консоли:

    Пример 5.6

    REM ФАЙЛ: "EX05006.BAS"
    REM ПРИМЕР ИСПОЛЬЗОВАНИЯ ФУНКЦИИ INPUT
    REM ВЫВОДИМ ПОДСКАЗКУ И ВВОДИМ СТРОКУ
    PRINT "Введите своё имя: ";
    INPUT NAME$
    PRINT "Рад познакомиться с Вами, "; NAME$
    STOP
    END

    5.3. Резюме

    Итак, Вы познакомились с основными "консольными" операциями по вводу-выводу символьных данных на консоль (клавиатуру и экран дисплея). Этот ввод-вывод является основным вводом-выводом MS-DOS, позволяющий "манипулировать" атрибутами дисплея и "прямым" чтением из буфера клавиатуры.

    Однако на практике чаще используется потоковый ввод-вывод. Именно его мы и рассмотрим в следующем разделе.

    Описание же скен-кодов клавиш клавиатуры и их обработки выходит за рамки данного курса.

    Страницы:

    Прямой ввод-вывод "на среднем уровне" позволяет вводить и выводить текстовые символы в файл, на консоль, модем, удалённый терминал. При этом в MS-DOS он организован при помощи механизма прерываний. Для иллюстрации "прямого ввода" будут приведены функции прямого ввода с консоли на языках Ассемблер и Си.

    5.1. Введение. Общие сведения о клавиатуре

    Клавиатура - это не просто "доска с клавишами" (дословный перевод английского названия "keyboard"). Помимо механической части (собственно клавиш и контактов) она включает в себя сложную топологию проводников, и даже собственный микропроцессор - контроллер.

    Задачи, решаемые этим микропроцессором, следующие:

  • Реагировать на нажатие клавиш или комбинации клавиш;
  • Реагировать на "длительное" нажатие на клавишу, и осуществлять соответствующие действия;
  • По положению клавиш генерировать специальный скен-код клавиши (см. раздел 5.1.1.);
  • В соответствии с "таблицей символов" (codepage) преобразовывать скен-код клавиши в ASCII символ, ей соответствующей;

    Примечание: некоторые "серые" (управляющие) клавиши не генерируют символы, а используются только для управления компьютером. В этом случае для них ASCII символ не генерируется (записывается в 0).

  • Заносит пару "ASCII код" - "Скен код" в клавиатурный буфер.
  • Видите, как всё сложно.… Теперь разберёмся с остальным.

    5.1.1. Понятие о скэн-кодах клавиш

    При нажатии клавиши на клавиатуре генерируется её скен-код. Скен-код можно считать "номером клавиши" на клавиатуре. Однако это не совсем так. Например, если скен код клавиши "F10" равен "68", то тот же код клавиши с нажатой клавишей "Shift" будет равен "93", с клавишей "Ctrl" - "103", а с клавишей "Alt" - "113".

    Существуют так называемые "алфавитно-цифровые" ("белые") и "управляющие" ("серые") клавиши. Первые генерируют скен-код и ASCII символы. Вторые генерируют только расширенный скен-код, а в поле "ASCII символа" стоит ноль. Благодаря этому программист легко может понять, как обрабатывать код клавиши: выводить ли символ на экран или запускать управляющую последовательность действий.

    Более подробно о скэн-кодах клавиш смотри [1, 8, 33].

    5.1.2. "Эхопечать" символов

    При вводе с клавиатуры пара значений "Скен-код" - "ASCII символ" заносится в клавиатурный буфер для её обработки на компьютере. При этом на старых терминалах все введённые символы отображались на экране дисплея. Но, с появлением персональных ЭВМ, выяснилось, что такой вывод ("Эхопечать") не всегда удобный. Что делать, если Вы вводите пароль, и Вы не хотите, чтобы кто-либо его прочитал с экрана? А что делать, если Вы используете алфавитно-цифровую клавишу как управляющую (например, при обработке меню)? В этом случае Вам помогут функции ввода с клавиатуры без эхопечати.

    5.1.3. Клавиатурный буфер

    Полученный в результате преобразований в контроллере двухбайтовый код посылается в кольцевой буфер ввода, который служит для синхронизации ввода данных с клавиатуры и приёма их выполняемой программой. Объём кольцевого буфера составляет 15 слов (30 байт). При этом буфер организован по принципу: "первым записан - первым считан" (английская аббревиатура "FIFO"). При переполнении буфера новые коды в него не поступают, а нажатие на клавиши вызывает предупреждающие сигналы.

    5.2. Основные функции для ввода символов с клавиатуры

    5.2.1. Консольный ввод в Ассемблере

    5.2.1.1. Ожидание ввода символа без эхопечати

    Функции 7 и 8 прерывания 21H ожидают ввода символа, если буфер клавиатуры пуст; появление символа на экране не отображается.

    Разница между этими функциями в том, что функция 8 распознаёт специальное сочетание клавиш "Ctrl+Break", а функция 7 игнорирует это сочетание клавиш.

    В обоих случаях функция возвращает символ в регистр AL. Если AL содержит ASCII 0, то получен расширенный код. Повторите вызов прерывания с теми же параметрами, и в AL появится второй байт расширенного кода.

    Пример 5.1

    ;--- получаем введённый символ
    	MOV AH,8	;номер функции
    	INT 21H
    	CMP AL,0		;проверяем на расширенный код
    	JE EXT_COD	;если да, то переход на анализ кода
    	…		; иначе - обработка символа
    ;--- анализ расширенного кода
    EXT_COD:	INT 21H	; берём второй байт кода
    	CMP AL, 75	; проверяем на "стрелка влево"
    	JE CUR_LEFT	; переход к обработчику
    	CMP AL, 77	; проверяем на "стрелка вправо"
    	JE CUR_RIGHT	; переход к обработчику

    5.2.1.2. Ожидание ввода символа с эхопечатью

    При вводе символов с эхопечатью эхо вводимых символов выдаётся на экран. При этом символы "возврат каретки" и "забой" переводятся в соответствующие перемещения курсора, а не отображаются как ASCII символы. Вывод эха осуществляется с текущей позиции на экране. При этом курсор после вывода смещается на позицию вправо, а по достижении правой границы экрана - переносит вывод на новую строку.

    Функция 1 прерывания 21H ожидает ввода символа с клавиатуры, если её буфер пуст, а затем выводит его на экран в текущую позицию курсора. Функция обрабатывает нажатие "Ctrl+Break". Введённый символ сохраняется в регистре AL. Если содержимое AL равно ASCII 0, то для получения расширенного кода повторите прерывание.

    Пример использования этой функции аналогичен примеру 5.1.

    Функция 1 прерывания 21H полностью игнорирует клавишу "Esc". Клавиши табуляции интерпретируются нормально. Клавиша "Backspace" сдвигает курсор на одну позицию влево, но не стирает символ в этой позиции. Клавиша "Enter" вызывает перемещение курсора на первую позицию строки (нет автоматического переноса на новую строку).

    5.2.1.3. Получение строки символов

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

    Функция 0AH прерывания 21H позволяет вводить строку длиной до 254 символов, выдавая эхо на терминал. Эта функция продолжает ввод символов до тех пор, пока не нажата клавиша "возврат каретки" ( "Enter" ). DS:DX указывает на строку, куда помещаются вводимые символы. При вводе первый байт этой позиции должен содержать число байтов, отводимой для этой строки. После того, как строка введена, второй байт даёт число реально введённых символов. Сама строка начинается с третьего байта.

    Надо отвести достаточно памяти для строки нужной длины, плюс 2 байта для дескриптора строки плюс 1 добавочный байт для символа "Возврат каретки". Код возврата каретки: ASCII 13, - вводится как последний символ строки, но не учитывается в результате, который функция помещает во второй байт дескриптора.

    Таким образом, для получения 50 - символьной строки надо отвести минимум 53 байта памяти, и поместить в первый байт памяти строки число ASCII 51. После ввода 50 символов второй байт дескриптора будет содержать ASCII 50, а 53-й байт отведённой памяти будет содержать ASCII 13.

    Пример 5.2.

    ; - - - в сегменте данных
    STRING	DB	53DUP(?) ; область для 50 символов
    ; - - - получение строки с клавиатуры
    	LEA DX, STRING	; DS:DX - адрес строки
    	MOV BX,DX
    	MOV AL,51	; установка длины строки (+1 для CR)
    	MOV [BX], AL	; посылаем в первый байт дескриптора
    	MOV AH, 0AH	; номер функции
    	INT 21H
    ; - - - получаем в AH длину строки
    	MOV AH, [BX]+1	; теперь длина в AH

    В этой процедуре можно использовать возможности редактирования строки MS-DOS. Нажатие "Backspace" или "стрелка влево" удаляет символ с экрана без помещения его в память. Работает клавиша табуляции, а расширенные коды игнорируются. Допускаются пустые строки (содержащие только символ ASCII 13). На дисплее при достижении правого края экрана вводимая строка переносится на следующую строку дисплея, а при достижении правого нижнего угла экран сдвигается на строку вверх. Если вводится больше символов, чем отведено для строки, лишние символы игнорируются, и включается гудок динамика.

    5.2.2. Консольный ввод в Си

    В языке Си и его разновидностях существуют следующие функции для ввода данных с клавиатуры:

  • cgets - чтение строки с консоли;
  • getch - чтение символа с консоли;
  • getche - чтение символа с эхопечатью;
  • kbhit - проверка нажатия клавиши на консоли;
  • cscanf - чтение данных с консоли по формату;
  • getpass - ввод с терминала пароля без эхопечати.
  • Рассмотрим некоторые из них поподробнее

    ФУНКЦИЯ GETCH
    ОПИСАНА В: <conio.h> /* Используется только для описания функции */
    СИНТАКСИС: int getch() ;
    НАЗНАЧЕНИЕ: Ввод одиночного символа с консольного терминала без эхопечати (без вывода на дисплей).
    ФУНКЦИЯ ВОЗВРАЩАЕТ: прочитанный символ.
    ПРИМЕЧАНИЕ: функция getch использует входной поток stdin.

    ФУНКЦИЯ GETCHE
    ОПИСАНА В: <conio.h> /* Используется только для описания функции */
    СИНТАКСИС: int getche();
    НАЗНАЧЕНИЕ: Функция читает одиночный символ с консольного терминала, с эхопечатью введённого символа на экране с текущей позиции курсора.
    ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: Прочитанный символ.
    ПРИМЕЧАНИЕ:
  • Нет ошибочных кодов возврата;
  • Функция корректно обрабатывает "Ctrl+Break".
  • ФУНКЦИЯ CGETS
    ОПИСАНА В: <conio.h> /* Используется только для описания функции */
    СИНТАКСИС:
    char *cgets(str);
    char *str;
    НАЗНАЧЕНИЕ: Функция читает строку символов непосредственно с консоли, и помещает строку и её длину по указателю str. Аргумент str должен быть указателем на массив символов;
    ЗАМЕЧАНИЕ: Первый элемент массива str[0] должен содержать максимально допустимую длину считываемой строки. Массив должен иметь достаточную длину, чтобы поместить строку, символ ASCII 0 плюс два байта дополнительно для дескриптора;

    cgets читает символы до тех пор, пока не прочитаны символы <CR> и <LF>, или пока не будет прочитано установленное число символов. Прочитанная строка начинается со str[2]. Если прочитана комбинация <CR>/<LF>, эти символы будут располагаться за символом конца строки (ASCII 0). Действительная длина строки помещается в str[1] ;

    ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: указатель на начало строки, то есть на адрес элемента str[2] ;
    ПРИМЕЧАНИЕ:
  • Нет кодов ошибок;
  • Длина строки ограничена ~ 250 символами.
  • В примере 5.3 иллюстрирована работа функции cgets.

    Пример 5.3

    /* Пример ex05003.c */
    /* Определение констант - длины буфера */
    #define LETTER_SIZE	50 // Максимальное число символов
    #define BUFF_SIZE		55 // Общий размер буфера = LETTER_SIZE+5
    /* Объявление переменной буфера */
    char *buff[BUFF_SIZE];
    char csize; //служебная переменна
    /* Подготовительные операции и чтение с клавиатуры в буфер */
    csize = BUFF_SIZE - 2; // Истинный размер буфера в символах
    buff[0] = csize;	// Заносим это значение в буфер;
    (void) cgets( buff );	// Читаем с клавиатуры в буфер
    if( buff[1] > LETTER_SIZE ) goto ERROR; // Если переполнение буфера, следуем за ссылкой ERROR.

    Благодаря механизму дескрипторов (двух первых "служебных" байтов строки-буфера) "переполнения буфера" достичь действительно сложно. MS-DOS просто не допустит чтения большего числа символов, чем это указано в байте buff[0].

    ФУНКЦИЯ CSCANF
    ОПИСАНА В: <conio.h>
    СИНТАКСИС:
    int cscanf( format {, argument} );
    char *format;
    НАЗНАЧЕНИЕ: Функция cscanf читает данные с консоли, выполняет форматные преобразования и помещает значения в переменные, адреса которых определяются аргументами функции.
    ЗАМЕЧАНИЯ:
  • Функция cscanf использует функцию getche для ввода символов;
  • Каждый аргумент должен указывать на переменную того типа, который соответствует её описанию в строке: "format". См. также описание функции scanf, в приложении №II (6.7) к лекции 6.
  • ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ:
  • количество элементов ввода, которые были успешно преобразованы и присвоены;
  • значение EOF, если встретился символ - признак конца файла;
  • 0, если нет присвоенных областей.
  • Пример: 5.4.

    int i; // Целая переменна
    /* Строка формата для ввода целого числа, состоящего из 10 знаков */
    const char format[] = "%10d";
    int ierr; // переменная ошибки ввода
    /* Ввод значения с консоли */
    ierr = cscanf( format, i );
    if( ierr != 1 )goto ERROR; // переход, если ошибка ввода
    ФУНКЦИЯ GETPASS
    ОПИСАНА В: <conio.h>
    СИНТАКСИС:
    char *getpass( prompt );
    char *prompt;
    НАЗНАЧЕНИЕ: Функция getpass используется для ввода с консольного терминала пароля, который оператор должен набрать в ответ на выводимое на экран приглашение, задаваемое строкой prompt с завершающим символом "\0". При вводе пароля отключается эхопечать вводимых с терминала символов.
    ЗАМЕЧАНИЕ: длина пароля составляет не более 8 символов.
    ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: указатель на строку с завершающим нулём, содержащую не более восьми первых символов текста.

    ПРИМЕЧАНИЕ: Пароль хранится в статическом буфере (символьном массиве) функции и переписывается при каждом обращении к ней.

    5.2.3. Консольный ввод в Quick Basic

    Для ввода символов в Quick Basic используются одна функция и один оператор:

  • функция INKEY$ - для ввода символов без эхопечати;
  • оператор INPUT - для ввода значений, в том числе и форматированный ввод.
  • Рассмотрим эти функции поподробнее

    ФУНКЦИЯ INKEY$
    СИНТАКСИС: INKEY$
    НАЗНАЧЕНИЕ: Функция для ввода символов с клавиатуры без эхопечати.
    ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ:
  • пустая строка - символ не введён;
  • один байт с ASCII - символом - нажата алфавитно-цифровая клавиша;
  • два байта - нажата "серая" клавиша.
  • При этом левый байт будет всегда нулевой, а правый содержит расширенный скен ход клавиши.

    Пример 5.5

    REM ФАЙЛ: "EX05005.BAS"
    REM ПРИМЕР ИСПОЛЬЗОВАНИЯ ФУНКЦИИ INKEY$
    REM ОЖИДАЕМ НАЖАТИЯ КЛАВИШИ
    100 C$ = INKEY$
    IF C$ = "" GOTO 100
    REM ПРОВЕРЯЕМ, НАЖАТА ЛИ "СЕРАЯ" КЛАВИША
    IF LEN( C$ ) = 2 GOSUB 2000
    …
    REM "КОНСТРУКЦИЯ" ДЛЯ ОБРАБОТКИ СКЕН КОДОВ
    2000 C$ = RIGHT( C$, 1 )
    REM ПРЕОБРАЗУЕМ КОД В ЧИСЛО
    C = ASC( C$)
    REM НАЖАТА КЛАВИША "СТРЕЛКА ВЛЕВО"
    IF C = 75 THEN GOSUB 3000
    REM НАЖАТА КЛАВИША "СТРЕЛКА ВПРАВО"
    IF C = 77 THEN GOSUB 3100
    …
    ОПЕРАТОР INPUT
    СИНТАКСИС: INPUT аргумент
    НАЗНАЧЕНИЕ: Оператор для форматированного ввода с клавиатуры с эхопечатью. Оператор автоматически переводит текст, набранный на клавиатуре, в формат, соответствующий формату вводимой переменной. Поддерживаются форматы:
  • вещественных чисел;
  • целых чисел;
  • символьных строк.
  • ЗАМЕЧАНИЯ:
  • Ввод символов завершается нажатием клавиши "Enter" ; код ASCII 13 не добавляется в конец строки;
  • До нажатия этой клавиши текст можно редактировать средствами редактирования строки DOS;
  • Оператор "не воспринимает" "серые клавиши" на клавиатуре. Реакция на такое нажатие зависит от реализации Бейсика.
  • ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: Нет.

    Приведём пример ввода строки символов с консоли:

    Пример 5.6

    REM ФАЙЛ: "EX05006.BAS"
    REM ПРИМЕР ИСПОЛЬЗОВАНИЯ ФУНКЦИИ INPUT
    REM ВЫВОДИМ ПОДСКАЗКУ И ВВОДИМ СТРОКУ
    PRINT "Введите своё имя: ";
    INPUT NAME$
    PRINT "Рад познакомиться с Вами, "; NAME$
    STOP
    END

    5.3. Резюме

    Итак, Вы познакомились с основными "консольными" операциями по вводу-выводу символьных данных на консоль (клавиатуру и экран дисплея). Этот ввод-вывод является основным вводом-выводом MS-DOS, позволяющий "манипулировать" атрибутами дисплея и "прямым" чтением из буфера клавиатуры.

    Однако на практике чаще используется потоковый ввод-вывод. Именно его мы и рассмотрим в следующем разделе.

    Описание же скен-кодов клавиш клавиатуры и их обработки выходит за рамки данного курса.

    Вернуться к учебному плану