Библиотека iostream по своей сути довольно сложная, поэтому мы не сможем охватить её полностью в рамках данных уроков. Тем не менее, мы можем рассмотреть её основной функционал. На этом уроке мы разберемся с классом istream
.
Примечание: Весь функционал объектов, которые работают с потоками ввода/вывода, находится в пространстве имен std. Это означает, что вам нужно либо добавлять префикс std::
ко всем объектам и функциям ввода/вывода, либо использовать строку using namespace std;
.
Оператор извлечения
Как вы уже узнали на предыдущем уроке, мы можем использовать оператор извлечения >>
для считывания информации из входного потока. Одной из наиболее распространенных проблем при считывании строк из входного потока является предотвращение переполнения. Например:
1 2 3 4 5 6 7 |
#include <iostream> int main() { char buf[12]; std::cin >> buf; } |
Что произойдет, если пользователь введет 20 символов? Правильно, переполнение. Одним из способов решения этой проблемы является использование манипуляторов. Манипулятор — это объект, который применяется для изменения потока данных с использованием операторов извлечения (>>
) или вставки (<<
).
Мы уже работали с одним из манипуляторов — endl, который одновременно выводит символ новой строки и удаляет текущие данные из буфера. Язык C++ предоставляет еще один манипулятор — setw() (из заголовочного файла iomanip), который используется для ограничения количества символов, считываемых из потока. Для использования setw() вам нужно просто передать в качестве параметра максимальное количество символов для извлечения и вставить вызов этого манипулятора следующим образом:
1 2 3 4 5 6 7 8 |
#include <iostream> #include <iomanip> int main() { char buf[12]; std::cin >> std::setw(12) >> buf; } |
Эта программа теперь прочитает только первые 11 символов из входного потока (+ один символ для нуль-терминатора). Все остальные символы останутся в потоке до следующего извлечения.
Извлечение и пробелы
Важный момент: оператор извлечения работает с «отформатированными» данными, т.е. он игнорирует все пробелы, символы табуляции и символ новой строки. Например:
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { char ch; while (std::cin >> ch) std::cout << ch; return 0; } |
Если пользователь введет следующее:
Hello! My name is Anton
То оператор извлечения пропустит все пробелы и символы новой строки. Следовательно, результат выполнения программы:
Hello!MynameisAnton
Часто пользовательский ввод все же нужен со всеми его пробелами. Для этого класс istream
предоставляет множество функций. Одной из наиболее полезных является фунция get(), которая извлекает символ из входного потока. Вот вышеприведенная программа, но уже с использованием функции get():
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { char ch; while (std::cin.get(ch)) std::cout << ch; return 0; } |
Теперь, если мы введем следующее:
Hello! My name is Anton
То получим:
Hello! My name is Anton
Функция get() также имеет строковую версию, в которой можно указать максимальное количество символов для извлечения. Например:
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { char strBuf[12]; std::cin.get(strBuf, 12); std::cout << strBuf << std::endl; return 0; } |
Если мы введем следующее:
Hello! My name is Anton
То получим:
Hello! My n
Обратите внимание, программа считывает только первые 11 символов (+ нуль-терминатор). Остальные символы остаются во входном потоке.
Один важный нюанс: функция get() не считывает символ новой строки! Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { char strBuf[12]; // Считываем первые 11 символов std::cin.get(strBuf, 12); std::cout << strBuf << std::endl; // Считываем дополнительно еще 11 символов std::cin.get(strBuf, 12); std::cout << strBuf << std::endl; return 0; } |
Если пользователь введет следующее:
Hello!
То получит:
Hello!
И программа сразу же завершит свое выполнение! Почему так? Почему не срабатывает второй ввод данных? Дело в том, что первый get() считывает символы до символа новой строки, а затем останавливается. Второй get() видит, что во входном потоке все еще есть данные и пытается их извлечь. Но первый символ, на который он натыкается — символ новой строки, поэтому происходит второй «Стоп!».
Для решения данной проблемы класс istream
предоставляет функцию getline(), которая работает точно так же, как и функция get(), но при этом может считывать символы новой строки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { char strBuf[12]; // Считываем 11 символов std::cin.getline(strBuf, 12); std::cout << strBuf << std::endl; // Считываем дополнительно еще 11 символов std::cin.getline(strBuf, 12); std::cout << strBuf << std::endl; return 0; } |
Этот код работает точно так, как ожидается, даже если пользователь введет строку с символом новой строки.
Если вам нужно узнать количество символов, извлеченных последним getline(), используйте функцию gcount():
1 2 3 4 5 6 7 8 9 10 11 |
#include <iostream> int main() { char strBuf[100]; std::cin.getline(strBuf, 100); std::cout << strBuf << std::endl; std::cout << std::cin.gcount() << " characters were read" << std::endl; return 0; } |
Результат:
Hello! My name is Anton
24 characters were read
Специальная версия функции getline() для std::string
Есть специальная версия функции getline(), которая находится вне класса istream
и используется для считывания переменных типа std::string. Она не является членом ни ostream
, ни istream
, а подключается заголовочным файлом string. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> #include <string> int main() { using namespace std; string strBuf; getline(cin, strBuf); cout << strBuf << endl; return 0; } |
Еще несколько полезных функций класса istream
Есть еще несколько полезных функций класса istream
, которые вы можете использовать:
функция ignore() — игнорирует первый символ из потока;
функция ignore(int nCount) — игнорирует первые nCount
(количество) символов из потока;
функция peek() — считывает символ из потока, при этом не удаляя его из потока;
функция unget() — возвращает последний считанный символ обратно в поток, чтобы его можно было извлечь в следующий раз;
функция putback(char ch) — помещает выбранный вами символ обратно в поток, чтобы его можно было извлечь в следующий раз.
Класс istream
содержит еще множество других полезный функций и их вариаций, но это уже тема для отдельного туториала.
День добрый!
Работаю в реплите.
Не понимаю слегка как работает в вашем примере getline, когда вписываю 12 символов в поток первый раз, второй раз гетлайн уже не выполняется и программа завершается, хоть первый раз гетлайн и выводит 11 символов в ch, где 12 это нуль терминатор разумеется, однако…
Если вводить 11 символов то второй раз getline выполняется.
Вопрос: Почему и как это работает в данном примере?
у меня эта проблема решилась только так
чуть-чуть кода пока пробовал по-разному сделать