Обработка ввода - Язык программирования C++ для спортивного программирования
Неделя 5Строки и Символы, Обработка текста, Обработка ввода
Обработка ввода
Как на самом деле читает cin >>: пропуск пробелов и переводов строки, числа, символы и слова; склеенные части вроде 23:57 или 7*6; целые строки через getline и перевод строки, который оставляет cin >>; чтение слов до конца ввода; \r из Windows; числа, слишком длинные для любого типа; и вывод, достаточно быстрый для 3 · 10⁵ строк.
Теория
Урок 8 из 10
13 мин чтения
Вопросов: 5
Программа может решить только ту задачу, которую правильно прочитала. Чаще всего
ввод — это обычные числа и слова, и cin >> читает их, не задумываясь. Но
бывает ввод, где части склеены (23:57, 7*6), где строки содержат пробелы и
где не сказано, сколько всего придёт. Этот урок о том, как прочитать ровно то,
что есть.
Что на самом деле делает cin >>
Каждый cin >> x делает две вещи: пропускает пробелы, табуляции и переводы
строки, а затем берёт столько символов, сколько подходит типу x:
Тип x
Берёт
Останавливается перед
int, long long
знак и цифры
первым символом, который не цифра
double
цифры, точку, цифры
первым символом, который не может быть частью числа
char
ровно один символ
—
string
символы
следующим пробелом или переводом строки
Отсюда два следствия, которые встречаются постоянно:
Для cin >> расположение ввода не важно.3 4 в одной строке и 3 и
4 в двух строках cin >> a >> b; читает одинаково.
Чтение из
берёт и оставляет ждать во вводе.
дальше
Практика: Обработка ввода
Целая строка, слова до конца ввода, время и уравнение, которые нужно разобрать, и 3 · 10⁵ запросов к калькулятору.
Второе следствие и позволяет разобрать склеенные части: прочитайте число, затем
прочитайте разделитель в char, чтобы убрать его с дороги, затем прочитайте
следующее число.
Даны текущее время и время школьного звонка, оба в виде HH:MM; звонок
позже в тот же день. Сколько минут осталось до звонка?
Ввод
Вывод
Те же чтения разбирают вычисление вроде 7*6 или 9 - 12:
cin >> a >> op >> b; с char op работает как с пробелами вокруг знака, так и
без них, потому что чтение char сначала пропускает пробелы, а чтение int
останавливается на знаке. Потом if (op == '+') … выбирает действие. (Читайте
a и b как long long, если они бывают до 10⁹: их произведение может быть
10¹⁸.)
Когда части — отдельные символы на известных местах, например уравнение
x+5=7, — прочитайте всё одной строкой и берите символы по индексу: e[1] —
знак, e[3] — =, а e[4] - '0' — значение последней цифры.
Целые строки
getline(cin, s) читает всё до конца строки, включая пробелы, и убирает перевод
строки из ввода. У него одна ловушка. После cin >> n; перевод строки послеn всё ещё ждёт во вводе — cin >> останавливается перед ним. getline сразу
после этого читает остаток той же строки, а он пустой.
Ввод
Код
Результат
3 ⏎ buy milk
cin >> n; getline(cin, s);
s — это "": остаток строки с 3
3 ⏎ buy milk
cin >> n; getline(cin >> ws, s);
s — это "buy milk"
cin >> ws пропускает все пробелы и переводы строки, и getline начинает с
первого настоящего символа. Пишите getline(cin >> ws, s) всегда, когда строка
идёт после cin >>, — и убирайте \r в конце, если строка может прийти из
файла Windows.
Дан список дел: число записей n, а затем n записей, по одной в строке.
Выведите записи от самой новой (последняя написанная — самая новая),
пронумеровав с 1.
Ввод
Вывод
Записи хранит vector<string> — вектор может хранить строки так же легко, как
числа.
Теперь побудьте программой, которая читает накладную курьера: для каждой строки
кода скажите, что она прочитает и где оставит ввод.
Прочитайте накладнуюпопробуй!
Чтение до конца ввода
Бывает ввод, в котором не сказано, сколько придёт слов или чисел: «дан текст».
cin >> w сам по себе условие — истинно, если что-то прочитано, и ложно, когда
ввод закончился, — поэтому while читает всё, что есть:
Дан текст в одной или нескольких строках. Посчитайте его слова и слова,
которые начинаются с заглавной буквы.
Ввод
Вывод
Для слов через пробел это проще, чем getline и разбиение: cin >> сам
пропускает все пробелы и переводы строки и никогда не сохраняет \r. Когда
вводите данные сами, закончите ввод через Ctrl+Z и Enter в Windows (Ctrl+D в
других системах); на проверяющей системе ввод просто заканчивается.
Конец строки в Windows
В файлах, сделанных в Windows, каждая строка заканчивается двумя символами,
\r и \n; в других системах — только \n. В тестах задачи может быть и так,
и так, поэтому программа должна работать с обоими:
cin >> считает \r пробелом: на числа, символы и слова он не влияет.
getline оставляет \r в конце строки. Если символы строки считают или
сравнивают, уберите его:
if (!s.empty() && s.back() == '\r') s.pop_back();
Числа, слишком длинные для любого типа
long long вмещает примерно до 9 · 10¹⁸ — 19 цифр. Число из ста цифр читают как
string и обрабатывают цифра за цифрой: его первая цифра — s[0], сумма цифр —
цикл по c - '0', а по последней цифре видно, чётное ли оно. Два таких числа
сравнивают сначала по длине, как в первом уроке.
Много строк на выход
С недели 2 каждая программа начинается с ios_base::sync_with_stdio(false); и
cin.tie(NULL);, которые делают cin и cout быстрыми. Ещё одна привычка
сохраняет их быстрыми: заканчивайте строки через "\n", а не endl. endl
вдобавок сбрасывает вывод — сразу отправляет его дальше, — и делать это для
каждой строки медленно. На нашей проверяющей системе решения учеников для
калькулятора с 3 · 10⁵ запросами работали около 0,1 секунды с "\n" и от 0,4 до
1 секунды с endl — на тестах побольше эта разница превращается в Time Limit
Exceeded.
Проверьте себя
А вы знали?
\r и \n пришли от пишущих машинок и телетайпов, которые появились после них.
В конце строки возврат каретки (\r) отодвигал каретку к левому краю, а
перевод строки (\n) прокручивал бумагу на одну строку — два отдельных
движения, поэтому и два отдельных кода. Windows до сих пор заканчивает строки
обоими, а Linux и macOS оставили только \n — и спустя шестьдесят лет программе,
которая читает строку, всё ещё приходится об этом помнить.
проверь себя1 балл
Ввод — 12:05. Что напечатает этот код?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Ввод — две строки: 2 и red apple. Что напечатает этот код?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Файл с тестом сделан в Windows, поэтому его первая строка хранится как символы a, b, c, \r, \n. Программа на проверяющей системе читает её через getline(cin, s);. Чему равно s.size()?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Ввод — 3 4 в первой строке и 5 во второй. Что напечатает этот код?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Почему решения, которые печатают много строк, заканчивают строки через "\n", а не endl?
Выберите один ответ
Войдите, чтобы ответить и сохранить прогресс.Войти
08:47
09:05
18
3
buy milk
call Aziz
fix the bike
1. fix the bike
2. call Aziz
3. buy milk
Ali and Bek met
Dana in Tashkent
7 4
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int h1, m1, h2, m2; char colon; // забирает ':' с дороги cin >> h1 >> colon >> m1; cin >> h2 >> colon >> m2; int now = h1 * 60 + m1; // минут с полуночи int bell = h2 * 60 + m2; cout << bell - now << "\n"; return 0;}
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int n; cin >> n; // перевод строки после n остаётся во вводе vector<string> notes(n); for(int i = 0; i < n; i++){ getline(cin >> ws, notes[i]); // ws: пропустить его, потом прочитать целую строку if(notes[i].back() == '\r'){ notes[i].pop_back(); // строка из файла Windows}} for(int i = n - 1; i >= 0; i--){ cout << n - i << ". "<< notes[i]<< "\n";} return 0;}
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); string w; int words = 0, names = 0; while(cin >> w){ // ложно, когда ввод закончился words++; if(isupper(w[0])){ names++;}} cout << words << " "<< names << "\n"; return 0;}
int h, m;char c;cin >> h >> c >> m;cout << h * 100 + m;
int n;string s;cin >> n;getline(cin, s);cout << s.size();