Потоковый ввод вывод c. Буферизированный (потоковый) ввод-вывод

02.08.2009 20:46

Предисловие
Едва ли какая-либо программа может обойтись без взаимодействия со своим окружением посредством ввода и вывода информации - иначе зачем она вообще нужна, если строго замкнута на себе, и не общается ни с пользователем, ни с окружающими ее данными и программами? Здесь мы рассмотрим, как программа, написанная на C++ может принимать информацию у пользователя и выдавать некие результаты своей работы ему же. Проще говоря, как она считывает данные с клавиатуры, и выводит их на экран.
Необходимо заметить, что подходы к выполнению этих операций менялись с течением времени. В языке C (Си) и ранних версиях C++, которые были еще во многим похожи на C, применялись функции printf, scanf и подобные им. С развитием языка C++ появились потоки, в том числе стандартные потоки ввода/вывода cin и cout. Что лучше использовать? Давайте посмотрим.
Потоки являются частью стандартной библиотеки C++ и являются мощным высокоуровневым средством. При использовании потоков ввода/вывода вы можете перегружать операторы >> и Таким образом, если вы пишите программы на современном C++ и при решении ваших задач вам нет необходимости использовать низкоуровневые средства, скорее всего, вам больше подойдут потоки.
Функции printf и scanf являются более низкоуровневыми, менее выразительными и требующими большего внимания при использовании средствами. Скорее всего, вам стоит остановить свой выбор на них, если вы используете C или же вы программируете на низком уровне и активно обращаетесь к аппаратным средствам.
Если же вы пишете в на языке C++, пользуясь устаревшей версией компилятора, то совет может быть только один - как можно скорее переходите на современные среды разработки с современными компиляторами и реализующими современную версию языка C++!

Стандартные потоки ввода/вывода
Стандартным потока ввода является поток cin (in put, ввод), а стандартным потоком вывода - cout (out put, вывод). Они определены в пространстве имен стандартной библиотеки std , поэтому для получения доступа к ним необходимо использовать префикс std:: - std::cin вместо cin и std::cout вместо cout . В качестве альтернативы можно использовать директиву using namespace std , чтобы объявить все имена из std глобальными. Однако, я бы посоветовал использовать первый вариант, если только вы не переписываете старый код на C++ или код на C для использования в новых версиях C++.
Также вам необходимо подключить заголовочный файл iostream , в котором и содержатся стандартные потоки ввода/вывода: #include .
Оператором ввода является >> ("прочесть из"), а оператором вывода - Рассмотрим теперь последовательно операции вывода и считывания информации с помощью потоков cout и cin .

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

выведет abc .
Понятное дело, что писать каждый раз std::cout

void f(int i)
{
std::cout }

Поток ввода cin
Для считывания информации, вводимой пользователем с клавиатуры, служит стандартный поток cin . Как и поток вывода, поток ввода работает с символьным представлением типов. То, как будут интерпретироваться вводимые символы, зависит от второго аргумента оператора >> (того, что справа). Соотетственно, вам не нужно указывать, какого типа данные предполагается считать, компилятор определит это исходя из типа переменной. Посмотрим в качестве примера программу, реализующую наипростейший калькулятор:

//Пример 5.1
#include

Int main()
{
int a, b, c;
char sign;

Std::cout std::cin >> a;
std::cout std::cin >> sign;
std::cout std::cin >> b;
switch(sign)
{
case "+": c = a + b; break;
case "-": c = a - b; break;
case "*": c = a * b; break;
case "/": c = a / b; break;
}
std::cout }

Выведя первое сообщение, программа ожидает, пока в потоке ввода не окажется что-нибудь, что можно будет считать в переменную типа int . Как только мы введем что-то и нажмем клавишу Enter, в поток передастся введенная нами строка. Если введенное удастся интерпретировать как символьное представление целого числа, программа благополучно запишет это число в переменную и продолжит свое выполнение. Если же была введена какая-нибудь не относящаяся к делу белиберда, то программа просто будет грязно ругаться по-английски (в зависимости от языка вашей реализации C++). Последнее, конечно, не удивительно, т.к. программа ожидает от программиста и пользователя разумного поведения - либо пользователь должен вводить все в точности как надо, либо программист должен предусмотреть "защиту от дурака".
Вернемся, однако, к нашим потокам. Итак, получив возможность считать в целую переменную соответствующее значение, программа ее таки считывает и выводит следующее сообщение, ожидая, что теперь ей введут один-единственный символ. Предположим, что пользователь действительно вводит один из символов +, -, * или / и вновь нажимает Enter. Тогда программа вновь успешно считывает этот символ в переменную sign и продолжает свою работу. Что будет дальше, вы должны уже смочь представить самостоятельно.
Следующее, что важно сказать, это то, что при считывании в переменные встроенных типов оператор >> считает концом ввода первый же встретившийся символ-разделитель . К таким символам относятся, например, пробел и символ перевода строки ("\n" , вводится нажатием клавиши Enter). Чтобы стало понятнее, что происходит, рассмотрим с этой позиции предыдущую программу.
Во-первых, ее код можно было бы переписать примерно следующим образом:

//Пример 5.2
#include

Int main()
{
int a, b, c;
char sign;

Std::cout std::cin >> a >> sign >> b;
switch(sign)
{
case "+": c = a + b; break;
case "-": c = a - b; break;
case "*": c = a * b; break;
case "/": c = a / b; break;
}
std::cout }

Если бы пользователь ввел (обязательно с пробелами!) строку 12 + 34 , то произошло бы следующее: после того, как была введена строка и нажата клавиша Enter, в поток ввода cin была бы передана строка 12 + 34 . Ожидающая момента, когда этот поток станет непустым, инструкция std::cin >> a считала бы фрагмент от начала строки до первого символ-разделителя. Т.е. было бы считано "слово" 12 , переведено в числовую форму и присвоено переменной a . Затем настал бы черед второго "слова" - того, что находилось в строке перед следующим пробелом. Символ + после этого оказывается считан, наступает черед "слова" 34 . Считав и его, программа завершает ввод, т.к. больше ей ничего не нужно узнавать, а если в потоке остались еще какие-то "лишние" "слова", то они окажутся просто невостребованы.
Итак, что мы видим? При вводе строки и нажатии клавиши Enter происходит запись данных в поток ввода . После этого ожидающие непустого потока инструкции начинают считывать из потока . Поэтому то, что вы нажали Enter, не значит, что все введенное считается единой строкой. Напротив, считываться будут отдельные "слова", и это можно использовать для ввода сразу нескольких переменных - нужно только разделить их пробелом.
Если же вам нужно считать целую строку, воспользуйтесь функцией getline:

void h()
{
std::string s;
getline(std::cin, s);
}

Продолжим разбор примера 5.1. Что же у нас "во-вторых"? А во-вторых у нас то, что если бы пользователь после того, как программа вывела Введите первое число, ввел бы 12 + 34 , то программа бы считала "слово" 12 , перевела бы его в числовую форму, присвоила значение переменной a ; затем бы вывела предложение ввести знак и... тут же бы его считала, поскольку в потоке уже есть данные для последующего считывания. Считав символ, она бы вывела строку Введите второе число, после чего считала бы "слово" 34 , не утруждая больше пользователя необходимостью прикасаться к клавиатуре. После чего бы все посчитала и вывела бы результат - строковые представления первого числа, знака, второго числа, знака равенства и числа-ответа, что выглядело бы как 12+34=46 .
Внешне бы это выглядело бы довольно странно: программа дважды просит что-то ввести, но ничего не считывает. Однако, на самом деле ей и не нужно больше ничего считывать - в потоке ввода уже есть все нужные программе данные. Если, конечно, пользователь не ввел то что нужно, а не какую-нибудь ерунду.
Словом, тут мы видим очередной пример того, что компьютер делает то, что ему сказано, а вовсе не обязательно то, что от него хотят. Поэтому важно понимать механику всего происходящего, чтобы эффективно нагружать компьютер работой.

Небольшое техническое замечание
Может создаться впечатление, что для ввода/вывода данных мы используем потоки подобно функциям, как например printf и scanf: вызываем его, указываем куда/откуда считывать данные, и получаем результат. На самом деле это не совсем так. Стандартные потоки, такие как cin и cout , являются классами, т.е. типами, определяемыми пользователем (в данном случае - создателями стандартной библиотеки). К механизмам взаимодействия с клавиатурой и монитором они подключаются совершенно независимо от нас, можно считать, что они есть независимо от указания в нашем коде строк наподобие cout > , определенные для классов потоков - они принимают в качестве аргументов конкретный поток и переменную, и записывают данные из одного в другое. Поток же является совокупностью хранимой в нем информации и способов работы с этой информации, в частности, операторов > .

О семействе функций printf в следующем уроке.

Стандартные библиотеки C ++ предоставляют расширенный набор возможностей ввода / вывода, которые мы увидим в последующих главах. В этой главе будут рассмотрены основные и наиболее распространенные операции ввода-вывода, необходимые для программирования на C ++.

C ++ I / O происходит в потоках, которые представляют собой последовательности байтов. Если байты поступают с устройства, такого как клавиатура, дисковод или сетевое соединение и т. д. В основную память, это называется операцией ввода, и если байты поступают из основной памяти в устройство, такое как экран дисплея, принтер, дисковод, или сетевое соединение и т. д., это называется операцией вывода .

Файлы заголовков библиотеки ввода-вывода

Для программ на C ++ важны следующие файлы заголовков -

Этот файл определяет объекты cin, cout, cerr и clog , которые соответствуют стандартным входным потокам, стандартным потокам вывода, потоку стандартной буферизации без буферизации и потоку стандартной буферизации, соответственно.

Этот файл объявляет услуги, полезные для выполнения отформатированного ввода / вывода с помощью так называемым параметризованным потоком манипуляторами, такие как setw и setprecision .

Этот файл объявляет службы для обработки файлов, управляемых пользователем. Мы обсудим это подробно в главе «Файл и поток».

Стандартный выходной поток (cout)

Предопределенный объект cout является экземпляром класса ostream . Сообщается, что объект cout «подключен к» стандартным устройствам вывода, который обычно является экраном дисплея. СоиЬ используется в сочетании с оператором вставки потока, который записывается в виде age;

Это будет эквивалентно следующим двум утверждениям:

Cin >> name; cin >> age;

Стандартный поток ошибок (cerr)

Предопределенный объект cerr является экземпляром класса ostream . Говорят, что объект cerr прикреплен к стандартным устройству ошибок, которое также является экраном дисплея, но объект cerr не забуферирован, и каждая вставка потока в cerr вызывает немедленный вывод его вывода.

Сегг

#include using namespace std; int main() { char str = "Unable to read...."; cerr

Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: