Авторизация



Счетчики

Обмен ссылками

Блог программиста
Качественный ремонт стиральной машины Новокузнецк сантехнические услуги.
Главная Учебник C++ Урок 5 - Ввод и вывод. Потоки.
Урок 5 - Ввод и вывод. Потоки. PDF Печать E-mail
Автор: Андрей   
02.08.2009 20:46

Предисловие
Стандартные потоки ввода/вывода
Поток вывода cout
Поток ввода cin
Небольшое техническое замечание

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

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

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

void f(int i)
 {
 std::cout << "Значение переменной i равно ";
 std::cout << i;
 std::cout << '\n';
 }

Здесь мы вначале вывели строку "Значение переменной i равно ", затем вывели переменную i, затем - символьный литерал '\n', означающий перевод строки. Обратите внимание на то, что \n является единой конструкцией, представляющей собой один символ, и потому '\n' является символьным литералом наравне с 'a', 'b', 'c', '+', '.', '4' и другими.
Также заметьте, что << направляет на вывод в точности то, что вы указали, не добавляя от себя ничего. Поэтому в предыдущем примере в конце строки стоит пробел, чтобы отделить выводимое следом значение переменной i, а следующая конструкция

void g()
 {
 std::cout << 'a';
 std::cout << 'b';
 std::cout << 'c';
 }

выведет abc.
Понятное дело, что писать каждый раз std::cout << крайне утомительно. К счастью, имеется очень простой выход из этого положения - результат операции вывода может также быть использован для дальнейшего вывода. Т.е. вышеприведенный пример можно записать значительно короче следующим образом:

void f(int i)
 {
 std::cout << "значение переменной i равно" << i << '\n';
 }

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

//Пример 5.1
#include <iostream>

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 << a << sign << b << '=' << c << '\n';
}

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

//Пример 5.2
#include <iostream>

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 << a << sign << b << '=' << c << '\n';
}

Если бы пользователь ввел (обязательно с пробелами!) строку 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 << 'a';. Реальную работу в таких случаях выполняют операторы << и >>, определенные для классов потоков - они принимают в качестве аргументов конкретный поток и переменную, и записывают данные из одного в другое. Поток же является совокупностью хранимой в нем информации и способов работы с этой информации, в частности, операторов << и >>.

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

Обновлено 16.05.2010 06:16