sonyps4.ru

C управление форматом вывода в поток. Потоковые классы

Потоки в C++ отличаются от функций ввода/вывода в C , обеспечивая работу как со стандартными потоками данных, так и с типами данных, определяемыми пользователем, а также обеспечивая единообразный и понятный синтаксис. Чтение данных из потока называется извлечением, а вывод данных в поток – включением. Поток в C++ - последовательность байтов, независимых от конкретного устройства, с которого производится считывание данных.

Обмен с потоком для увеличения скорости передачи данных производится через специальную область памяти – буфер. Передача данных выполняется при выводе после заполнения буфера и при вводе, если буфер исчерпан. По направлению обмена данных потоки делят на три группы:

  • входные потоки (позволяющие вводить данные в память)
  • выходные потоки (осуществляющие вывод данных из памяти)

В зависимости от вида устройства, с которым работает поток данных, их делят на:

  • стандартные потоки
  • файловые потоки
  • строковые потоки

Стандартные потоки предназначены для передачи данных с клавиатуры на экран (это: stdin - стандартный поток ввода данных, stdout - стандартный поток вывода данных и stderr - стандартный поток ошибок). Файловые потоки – для обмена информацией с файлами. Строковые потоки – для работы с массивами символов в оперативной памяти. Для поддержки этих потоков в C++ стандартная библиотека содержит иерархию классов, построенную на основе двух базовых классов:

  • ios – базовый класс, содержащий общие для ввода/вывода поля и методы
  • streambuf – обеспечивает буферизацию потоков и их взаимодействие с физическими устройствами.

От этих базовых классов наследуются классы istream и ostream для входных и выходных потоков соответственно. Эти потоки являются базовыми для iostream , который позволяет реализовывать двунаправленные потоки. Ниже в иерархии находятся файловые и строковые потоки:

  • isstrinstream – класс входного строкового потока
  • osstringstream – класс выходного строкового потока
  • stringstream – класс двунаправленного строкового потока
  • ifsteam – класс входных файловых потоков
  • ofstream – класс выходных файловых потоков
  • fstream – класс двунаправленных файловых потоков

Стандартный поток

Чтобы использовать стандартные потоки ввода-вывода нужно включать заголовочный файл .Заголовочный файл кроме описания потоков ввода-вывода содержит описание ещё и предопределенных объектов.


Таблица 1

Объект Класс Описание
cin istream связывается с клавиатурой (со стандартным буфером ввода)
cout ostream связывается с экраном (со стандартным буфером вывода)
cerr ostream связывается с экраном (стандартный не буферизованный вывод, куда направляются сообщения об ошибках)
clog ostream связывается с экраном (стандартный буферизованный вывод, куда направляются сообщения об ошибках)

Эти объекты создаются при включении в программу файла iostream . При этом становятся доступными средства ввода-вывода. Соответствующие операции > определены путем перегрузки операции сдвига.


#include int main() { int i; cin>>i;

cout 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++ имеется набор классов для управления вводом-выводом.

В отличие от функций буферизованного ввода-вывода С (таких, как print f и scanf, не выполняющих никаких проверок на соответствие аргументов форматной строке) классы потоков C++ безопасны в отношении типа. Ввод-вывод использует механизм перегрузки операций, гарантирующий вызов нужной функции-операции для указанного типа данных. Это главное преимущество потоков языка C++.

Классы потоков

К классам потоков относятся следующие:

  • Класс streambuf управляет буфером потока, обеспечивая базовые операции заполнения, опорожнения, сброса и прочих манипуляций с буфером.
  • Класс ios является базовым классом потоков ввода-вывода.
  • Классы istream и ostream — производные от ios и обеспечивают работу потоков соответственно ввода и вывода.
  • Класс iоstream является производным от двух предыдущих и предусматривает функции как для ввода, так и для вывода.
  • Классы ifstream, of stream и f stream предназначены для управления файловым вводом-выводом.
  • Классы istrstream и ostrstream управляют резидентными потоками (форматированием строк в памяти). Это устаревшая методика, оставшаяся в C++Builder в качестве пережитка.

Для работы с потоками вам потребуется включить в программу заголовочный файл iostream.h. Кроме того, может потребоваться подключить файлы fstream.h (файловый ввод-вывод), iomanip.h (параметризованные манипуляторы) и strstream.h (форматирование ь памяти).

Предопределенные потоки

Библиотека ввода-вывода C++ предусматривает четыре предопределенных объекта-потока, связанных со стандартными входным и выходным устройствами. Ниже дана сводка этих объектов.

Таблица 9.1. Предопределенные объекты-потоки C++

Имя

Класс

Описание

istream

Ассоциируется со стандартным вводом (клавиатурой).

cout

ostream

Ассоциируется со стандартным выводом (экраном).

cerr

ostream

Ассоциируется со стандартным устройством ошибок (экраном) без буферизации.

clog

ostream

Ассоциируется со стандартным устройством ошибок (экраном)с буферизацией.

Операции извлечения и передачи в поток

Основными классами ввода-вывода C++ являются istream и ostream. Первый из них перегружает операцию правого сдвига (>>), которая служит в нем для ввода данных и называется операцией извлечения из потока. Класс ostream перегружает соответственно операцию левого сдвига (

Загрузка...