Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
0 / 0 / 0
Регистрация: 02.03.2016
Сообщений: 36

Как происходит ввод и обработка символов из консоли?

02.03.2016, 15:52. Показов 1509. Ответов 15
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Приветствую, не могли бы вы мне подсказать на счет одной вещи. В книге Страуструпа есть реализация калькулятора, которая основана на том, что программа сначала анализирует все символы, принимает решение о первичности вычислений и вычисляет.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
class Token {
public:
    char kind;        // what kind of token
    double value;     // for numbers: a value 
    Token(char ch)    // make a Token from a char
        :kind(ch), value(0) { }    
    Token(char ch, double val)     // make a Token from a char and a double
        :kind(ch), value(val) { }
};
 
//------------------------------------------------------------------------------
 
class Token_stream {
public: 
    Token_stream();   // make a Token_stream that reads from cin
    Token get();      // get a Token (get() is defined elsewhere)
    void putback(Token t);    // put a Token back
private:
    bool full;        // is there a Token in the buffer?
    Token buffer;     // here is where we keep a Token put back using putback()
};
 
//------------------------------------------------------------------------------
 
// The constructor just sets full to indicate that the buffer is empty:
Token_stream::Token_stream()
:full(false), buffer(0)    // no Token in buffer
{
}
 
//------------------------------------------------------------------------------
 
// The putback() member function puts its argument back into the Token_stream's buffer:
void Token_stream::putback(Token t)
{
    if (full) perror("putback() into a full buffer");
    buffer = t;       // copy t to buffer
    full = true;      // buffer is now full
}
 
//------------------------------------------------------------------------------
 
Token Token_stream::get()
{
    if (full) {       // do we already have a Token ready?
        // remove token from buffer
        full=false;
        return buffer;
    } 
 
    char ch;
    cin >> ch;    // note that >> skips whitespace (space, newline, tab, etc.)
 
    switch (ch) {
    case '=':    // for "print"
    case 'x':    // for "quit"
    case '(': case ')': case '+': case '-': case '*': case '/': case '{': case '}': case '!':
        return Token(ch);        // let each character represent itself
    case '.':
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
        {    
            cin.putback(ch);         // put digit back into the input stream
            double val;
            cin >> val;              // read a floating-point number
            return Token('8',val);   // let '8' represent "a number"
        }
    default:
        perror("Bad token");
    }
}
 
//------------------------------------------------------------------------------
 
Token_stream ts;        // provides get() and putback() 
 
//------------------------------------------------------------------------------
 
double expression();    // declaration so that primary() can call expression()
double primary2();
 
//------------------------------------------------------------------------------
 
// deal with numbers and parentheses
double primary()
{
    Token t = ts.get();
    switch (t.kind) {
    case '(':    // handle '(' expression ')'
        {    
            double d = expression();
            t = ts.get();
            if (t.kind != ')') perror("')' expected");
            return d;
        }
 
         case '{':    // handle '(' expression ')'
        {    
            double d = expression();
            t = ts.get();
            if (t.kind != '}') perror("'}' expected");
            return d;
        }
    case '8':            // we use '8' to represent a number
        return t.value;  // return the number's value
    default:
        perror("primary expected");
    }
}
 
 
// deal with *, /, and %
double term()
{
    double left = factorial();
    Token t = ts.get();        // get the next token from token stream
 
    while(true) {
        switch (t.kind) {
        case '*':
            left *= primary();
            t = ts.get();
        case '/':
            {    
                double d = primary();
                if (d == 0) perror("divide by zero");
                left /= d; 
                t = ts.get();
                break;
            }
        default: 
            ts.putback(t);     // put t back into the token stream
            return left;
        }
    }
}
 
//------------------------------------------------------------------------------
 
// deal with + and -
double expression()
{
    double left = term();      // read and evaluate a Term
    Token t = ts.get();        // get the next token from token stream
 
    while(true) {    
        switch(t.kind) {
        case '+':
            left += term();    // evaluate Term and add
            t = ts.get();
            break;
        case '-':
            left -= term();    // evaluate Term and subtract
            t = ts.get();
            break;
        default: 
            ts.putback(t);     // put t back into the token stream
            return left;       // finally: no more + or -: return the answer
        }
    }
}
 
//------------------------------------------------------------------------------
 
int main()
try
{
    setlocale(LC_ALL,"Rus");
    cout<<" Добро пожаловать в программу калькулятор.\n Пожалуйста, введите выражения, содержашие числа с плавающей точкой.\n Программа может работать с символами * / + -.\n Для выхода из программы введите x.\n Для вывода ответа введите знак = после выраженияю\n";
    double val=0;
    while (cin) {
        Token t = ts.get();
 
        if (t.kind == 'x') break; // 'x' for quit
        if (t.kind == '=')        // '=' for "print now"
            cout << "=" << val << '\n';
        else
            ts.putback(t);
        val = expression();
    }
    getch();
}
catch (exception& e) {
    cerr << "error: " << e.what() << '\n'; 
    getch();
    return 1;
}
catch (...) {
    cerr << "Oops: unknown exception!\n"; 
    getch();
    return 2;
}

Вопрос вот в чем. Как программа успевает все это проанализировать? Ведь кнопку Enter мы не нажимаем в консоли пока вводим эти числа, а по коду она предлагает эти символы каждый раз ввести. Как я понял, во время того как мы вводим символ в консоль он тут же передается программе на анализ, возвращается и мы этого даже не замечая продолжаем ввод?
Иначе бы это было так: ввести 1 символ, нажать Enter, снова ввести символ, нажать Enter и т.д.
Объясните пожалуйста суть как он анализирует. Остальное понятно, кроме этого.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
02.03.2016, 15:52
Ответы с готовыми решениями:

Ввод символов из консоли
Всем привет! Подскажите пожалуйста почему, когда мы через консоль (путем драгЭдропа) указываем адрес файла, и этот адрес имеет пробел,...

Ввод русских символов в консоли
Создал консольное приложение, написал примерно вот такой код: int _tmain(int argc, _TCHAR* argv) { setlocale...

Ввод строки из 10 символов в консоли
Необходимо ввести в консольном приложении строку из 10 символов , и сделать так чтобы программа выводила эту же строку из 10 символов ,но...

15
 Аватар для avgoor
1550 / 877 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
02.03.2016, 16:06
DSLS, Когда вы жмете enter, в потоке оказывается строка, которую потом забираете оттуда посимвольно.
0
0 / 0 / 0
Регистрация: 02.03.2016
Сообщений: 36
02.03.2016, 16:11  [ТС]
Просто, как я понимаю, функция Get() это и есть тот самый ввод.

C++
1
2
char ch;
    cin >> ch;
эта часть кода означает, что мы создали символьную переменную, и предложили ее ввести. Но ведь выражение мы вводим не по одному символу, а сразу целиком. Каким образом оно его разбирает? Логично бы было, что каждый раз оно предлагало бы нам ввести по одному символу пока он не сложится в полное выражение.

Ведь если там в коде мы введем "X" оно выйдет из программы, значит все таки, чтобы обработать его нужно нажатие клавиши.

Вот это вот немного непонятно, ведь всегда когда предлагает cin'ом ввести, следующее предложение ввести будет после того как этот ввод будет завершен, а тут как-то по другому.
0
 Аватар для avgoor
1550 / 877 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
02.03.2016, 16:15
DSLS, Пусть надо ввести не char.
C++
1
2
3
int i;
std::string s;
std::cin>>i>>s;
Вы вводите "100ололо".
Чему равны i и s?
0
1378 / 522 / 72
Регистрация: 21.07.2015
Сообщений: 1,308
02.03.2016, 16:16
DSLS,строка буферизируется.
Цитата Сообщение от DSLS Посмотреть сообщение
в коде мы введем "X" оно выйдет из программы
X + Enter.
0
0 / 0 / 0
Регистрация: 02.03.2016
Сообщений: 36
02.03.2016, 16:19  [ТС]
avgoor, по всей видимости в i запишется 100, а в s ололо. Но разве не нужно, чтобы именно в i записалось 100 нажать после 100 enter? Оно же вроде выдаст ошибку, что "100ололо" не соответствует типу int?
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
02.03.2016, 16:22
Цитата Сообщение от DSLS Посмотреть сообщение
Но ведь выражение мы вводим не по одному символу, а сразу целиком.
Все лишнее остается во внутреннем буфере. С точки зрения программы, в этом буфере вообще изначально лежит все что было и все что будет, до самого конца времен (конца потока). На практике это не так, но программе это не видно (если конца времен еще не наступило, cin подождет когда он наступит).
0
 Аватар для avgoor
1550 / 877 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
02.03.2016, 16:23
Цитата Сообщение от DSLS Посмотреть сообщение
Оно же вроде выдаст ошибку, что "100ололо" не соответствует типу int?
Не выдаст. Выдало бы если вы ввели "ололо100". Представьте что вы читаете не из cin а из файла (у Страуструпа в примерах есть такая модификация "калькулятора"). cin от файла на диске по поведению отличается только тем, что если в нем не осталось символов, он предложит их ввести. По вашему из файла можно прочитать int только если в него больше ничего не записано?
0
0 / 0 / 0
Регистрация: 02.03.2016
Сообщений: 36
02.03.2016, 16:28  [ТС]
Хм, то есть

C++
1
2
while (cin) {
Token t = ts.get();
можно представить как cin>>ch>>ch>>ch? Если да, то как они не заменяют друг друга и сохраняются все? Это тот самый буфер, о котором написал Renji?
0
1378 / 522 / 72
Регистрация: 21.07.2015
Сообщений: 1,308
02.03.2016, 16:32
Цитата Сообщение от DSLS Посмотреть сообщение
можно представить как cin>>ch>>ch>>ch?
Нельзя.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
02.03.2016, 16:33
Цитата Сообщение от DSLS Посмотреть сообщение
можно представить как cin>>ch>>ch>>ch? Если да, то как они не заменяют друг друга и сохраняются все?
get читает символ сразу и знает о специальном символе EOF влезающем только в int. >> сначала пропускает все лидирующие пробелы и EOF не умеет. В остальном да, никакой разницы.
0
 Аватар для avgoor
1550 / 877 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
02.03.2016, 16:33
Цитата Сообщение от DSLS Посмотреть сообщение
Если да, то как они не заменяют друг друга и сохраняются все?
Они заменяют друг друга. Просто перед заменой с ними что-то делается.
C++
1
2
while(cin>>ch)
    std::cout<<"'"<<ch<<"' ";
0
1378 / 522 / 72
Регистрация: 21.07.2015
Сообщений: 1,308
02.03.2016, 16:34
Цитата Сообщение от DSLS Посмотреть сообщение
то тот самый буфер, о котором написал Renji?
t - это не буфер. Буфер, упрощенного говоря, внутри cin.
0
0 / 0 / 0
Регистрация: 02.03.2016
Сообщений: 36
02.03.2016, 16:36  [ТС]
Например, выражение 2+2.

Первая цифра 2 это предложил нам ввести ch. мы ввели этот символ. Куда он девается? Ведь дальше нам нужно в тот же самый Ch ввести уже +. Или эта цифра 2 сначала прогоняется через все функции, в left записывает цифру 2. Потом снова предлагает ввести ch, видит что там плюс и действует по сценарию плюса, то есть уже снова считывает ch, получает оттуда число, но так как оно действует по сценарию плюса оно в left записывает значение +2, а так как оно уже до этого хранило в себе 2, то left станет 4?
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
02.03.2016, 16:42
Цитата Сообщение от DSLS Посмотреть сообщение
Первая цифра 2 это предложил нам ввести ch. мы ввели этот символ. Куда он девается?
В случае cin>>ch>>ch>>ch - цифра улетает в трубу. Буфер ввода спрятан внутри cin, а не создается программистом. Вы вводите "2+2", cin отдает программе первый символ, остальные держит в уме. Если вы просите у cin больше чем у него в уме, он выдает приглашение "введи еще символов". Но программе не видно когда это приглашение вылезает и ей кажется что все в cin лежало изначально.
0
 Аватар для avgoor
1550 / 877 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
02.03.2016, 16:43
Цитата Сообщение от DSLS Посмотреть сообщение
Например, выражение 2+2.
Упрощенно
в expression() в left попадает 2 затем в t попадает "+" затем возвращается значение left+term(). term возвращает вторую 2. каждый раз ch внутри gettoken пререзаписывается.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
02.03.2016, 16:43
Помогаю со студенческими работами здесь

Запрет на ввод символов в консоли
Ввобщем всем привет, хотелось бы чтобы вы мне помогли. Нужно реализовать запрет ввода символов в консоле. Вот то, что я смог осилить взяв...

Ограничение на ввод символов в консоли
Всем добрый день! Столкнулся с такой проблемой: Мне нужно ввести 300 целых чисел в одной строке в консоле, но после вводы нескольких...

Ввод русских символов в char* с консоли
Доброго времени года! Прописал setlocale для норм русского в консоли. Имею строку типа char*. Когда инициализирую ее в проге, и вывожу...

Ввод и обработка символов
Здравствуйте. Вот такая задача 1) Из клавиатуры вводятся символы, пока не встретится символ *. На экран выводить код каждого введенного...

Как правильно происходит обработка функций
Привет всем. Пример: Есть некая переменная N; она хранит в себе некую инфу. Например считанную строку с базы данных. есть...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Новые блоги и статьи
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru