3. Функция ввода

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

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

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

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

Первая сложность состоит в том, что символ конца строки 'n' важен для калькулятора, но потоковые функции ввода воспринимают его как символ обобщенного пробела. Иначе говоря, для этих функций 'n' имеет значение только как символ, завершающий лексему.

Поэтому приходится анализировать все обобщенные пробелы (пробел, табуляция и т.п.). Это делается в операторе do :

char ch;

do { // пропускает пробелы за исключением 'n'

if(!cin.get(ch)) return curr_tok = END;

} while (ch!='n' && isspace(ch));

Функция cin.get(ch) читает один символ из стандартного входного потока в ch. Значение условия if(!cin.get(ch)) - ложь, если из потока cin нельзя получить ни одного символа. Тогда возвращается лексема END, чтобы закончить работу калькулятора. Операция ! (NOT) нужна потому, что в случае успешного считывания get() возвращает ненулевое значение.

Функция-подстановка isspace() из <ctype.h> проверяет, не является ли ее параметр обобщенным пробелом. Она возвращает ненулевое значение, если является, и нуль в противном случае. Проверка реализуется как обращение к таблице, поэтому для скорости лучше вызывать isspace(), чем проверять самому. То же можно сказать о функциях isalpha(), isdigit() и isalnum(), которые используются в get_token().

После пропуска обобщенных пробелов следующий считанный символ определяет, какой будет начинающаяся с него лексема. Прежде, чем привести всю функцию, рассмотрим некоторые случаи отдельно. Лексемы 'n' и ';', завершающие выражение, обрабатываются следующим образом:

switch (ch) {

case ';':

case 'n':

cin >> ws; // пропуск обобщенного пробела

return curr_tok=PRINT;

Необязательно снова пропускать пробел, но, сделав это, мы избежим повторных вызовов функции get_token(). Переменная ws, описанная в файле <stream.h>, используется только как приемник ненужных пробелов.

Ошибка во входных данных, а также конец ввода не будут обнаружены до следующего вызова функции get_token(). Обратите внимание, как несколько меток выбора помечают одну последовательность операторов, заданную для этих вариантов. Для обоих символов ('n' и ';') возвращается лексема PRINT, и она же помещается в curr_tok.

Числа обрабатываются следующим образом:

case '0': case '1': case '2': case '3': case '4':

case '5': case '6': case '7': case '8': case '9':

case '.':

cin.putback(ch);

cin >> number_value;

return curr_tok=NUMBER;

Поскольку оператор >> может читать константу с плавающей точкой типа double, программа тривиальна: прежде всего начальный символ (цифра или точка) возвращается назад в cin, а затем константу можно считать в number_value. Имя, т.е. лексема NAME, определяется как буква, за которой может идти несколько букв или цифр:

if (isalpha(ch)) {

char* p = name_string;

*p++ = ch;

while (cin.get(ch) && isalnum(ch)) *p++ = ch;

cin.putback(ch);

*p = 0;

return curr_tok=NAME;

}

Этот фрагмент программы заносит в name_string строку, оканчивающуюся нулевым символом. Функции isalpha() и isalnum() определены в <ctype.h>.

Результат isalnum(c) ненулевой, если c - буква или цифра, и нулевой в противном случае.

Приведем функцию ввода полностью:

token_value get_token()

{

char ch;

do { // пропускает обобщенные пробелы за исключением 'n'

if(!cin.get(ch)) return curr_tok = END;

} while (ch!='n' && isspace(ch));

switch (ch) {

case ';':

case 'n':

cin >> ws; // пропуск обобщенного пробела

return curr_tok=PRINT;

case '*':

case '/':

case '+':

case '-':

case '(':

case ')':

case '=':

return curr_tok=token_value(ch);

case '0': case '1': case '2': case '3': case '4':

case '5': case '6': case '7': case '8': case '9':

case '.':

cin.putback(ch);

cin >> number_value;

return curr_tok=NUMBER;

default: // NAME, NAME= или ошибка

if (isalpha(ch)) {

char* p = name_string;

*p++ = ch;

while (cin.get(ch) && isalnum(ch)) *p++ = ch;

cin.putback(ch);

*p = 0;

return curr_tok=NAME;

}

error("недопустимая лексема");

return curr_tok=PRINT;

}

}

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


Информация о работе «Системное программное обеспечение»
Раздел: Информатика, программирование
Количество знаков с пробелами: 22151
Количество таблиц: 0
Количество изображений: 0

Похожие работы

Скачать
112819
0
0

... . Объясните, для чего служат разрешения и привилегии в Windows NT. Зав. кафедрой --------------------------------------------------   Экзаменационный билет по предмету СИСТЕМНОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ Билет № 22 Перечислите возможности и инструменты системы программирования Microsoft Developer Studio. Укажите для чего предназначается буфер в системах ввода-вывода, ...

Скачать
18733
3
3

... доступа к информации, ее целостности и другие возможности использования сетевых ресурсов. 3. Сервисное ПО: краткая характеристика Сервисное программное обеспечение - программы и программные комплексы, которые расширяют возможности базового программного обеспечения и организуют более удобную среду работы пользователя. Это набор сервисных, дополнительно устанавливаемых программ, которые ...

Скачать
7680
0
1

... . John W. Tukey) в 1958 году.[1] Совокупность программ, предназначенная для решения задач на ПК, называется программным обеспечением. Состав программного обеспечения ПК называют программной конфигурацией. Программное обеспечение, можно условно разделить на три категории: ·     системное ПО (программы общего пользования), выполняющие различные вспомогательные функции, например создание копий ...

Скачать
12764
1
0

я правилами вывода, а лишь служат для отражения семантической и синтаксической стороны грамматики. Для наглядного изображения работы программы представлено дерево функционального вызова (рис 1). На нём можно проследить принцип рекурсивного спуска -основной принцип, заложенный в обработку. Он заключается в прохождении дерева от крайней левой до крайней правой вершины дерева. Кроме того, для ...

0 комментариев


Наверх