Потоки в
C++ отличаются от функций ввода/вывода в
C, обеспечивая работу как со
стандартными потоками данных, так и с типами данных, определяемыми пользователем, а также обеспечивая единообразный и понятный синтаксис. Чтение данных из потока называется извлечением, а вывод данных в поток – включением. Поток в
C++ - последовательность байтов, независимых от конкретного устройства, с которого производится считывание данных.
Обмен с потоком для увеличения скорости передачи данных производится через специальную область памяти – буфер. Передача данных выполняется при выводе после заполнения буфера и при вводе, если буфер исчерпан. По направлению обмена данных потоки делят на три группы:
В зависимости от вида устройства, с которым работает поток данных, их делят на:
- стандартные потоки
- файловые потоки
- строковые потоки
Стандартные потоки предназначены для передачи данных с клавиатуры на экран (это:
stdin - стандартный поток ввода данных,
stdout - стандартный поток вывода данных и
stderr - стандартный поток ошибок).
Файловые потоки – для обмена информацией с файлами.
Строковые потоки – для работы с массивами символов в оперативной памяти. Для поддержки этих потоков в C++ стандартная библиотека содержит иерархию классов, построенную на основе двух базовых классов:
- ios – базовый класс, содержащий общие для ввода/вывода поля и методы
- streambuf – обеспечивает буферизацию потоков и их взаимодействие с физическими устройствами.
От этих базовых классов наследуются классы
istream и
ostream для входных и выходных потоков соответственно. Эти потоки являются базовыми для
iostream, который позволяет реализовывать двунаправленные потоки. Ниже в иерархии находятся файловые и строковые потоки:
- isstrinstream – класс входного строкового потока
- osstringstream – класс выходного строкового потока
- stringstream – класс двунаправленного строкового потока
- ifsteam – класс входных файловых потоков
- ofstream – класс выходных файловых потоков
- fstream – класс двунаправленных файловых потоков
Стандартный поток
Чтобы использовать стандартные потоки ввода-вывода нужно включать заголовочный файл <iostream.h>.Заголовочный файл <iostream.h> кроме описания потоков ввода-вывода содержит описание ещё и предопределенных объектов.
Таблица 1
Объект | Класс | Описание |
cin | istream | связывается с клавиатурой (со стандартным буфером ввода) |
cout | ostream | связывается с экраном (со стандартным буфером вывода) |
cerr | ostream | связывается с экраном (стандартный не буферизованный вывод, куда направляются сообщения об ошибках) |
clog | ostream | связывается с экраном (стандартный буферизованный вывод, куда направляются сообщения об ошибках) |
Эти объекты создаются при включении в программу файла
iostream. При этом становятся доступными средства ввода-вывода. Соответствующие операции << и >> определены путем
перегрузки операции сдвига.
Пример 1
#include<iostream.h>
int main()
{
int i;
cin>>i;
cout << ”Output i = ”<< i;
return 0;
}
Операции извлечения и чтения в качестве результата своего выполнения формирует ссылку на объект типа
istream для извлечения и на объект типа
ostream для чтения.
Файловые потоки.
Под
файлом обычно подразумевается поименованная информация на внешнем носителе. Практически в
С++ файл рассматривается как последовательный поток байтов. Каждый файл завершается маркером
EOF или указанием числа байтов, записанных в служебную структуру данных, поддерживаемую соответствующей системой.
По способу доступа файлы делятся на последовательные и файлы с произвольным доступом.
Файлы с последовательным доступом – файлы, в которых чтение и запись производятся сначала, байт за байтом.
Файлы с произвольным доступом допускают чтение и запись в произвольной позиции. Стандартная библиотека содержит три класса для работы с файлами. Все три класса являются производными от классов :
Наследуют перегруженные операции <<, >>. Использование
файловых потоков в программе предполагает выполнение следующих операций:
1) создание потока
2) открытие потока и связывание его с файлом
3) обмен данных (ввод / вывод)
4) уничтожение потока
5) закрытие файла
Каждый класс файловых потоков содержит
конструкторы, с помощью которых можно создавать объекты этих классов различными способами. Конструкторы без параметров создают объект соответствующего класса, не связывая его с файлом. Конструктор с параметром создает объект соответствующего класса, открывает файл с указанным именем и связывает файл с объектом.
ifstream(char *name, int mode = ios::in);
ofstream(char *name, int mode = ios::out);
fstream(char *name, int mode = ios::in [ios::out] );
Вторым параметром
конструктора является режим открытия файла. Если установленное по умолчанию значение не устраивает программиста, можно указать другой, составив его из битовых масок, определенных в классе ios.
Таблица 2
in | = 0x01; //открыт для чтения |
out | = 0x02; // открыт для записи |
ate | = 0x04; // установить указатель на конец файла |
app | = 0x08; // открыть на добавление в конец |
trune | =0x19; // если файл существует, удалить |
nocreate | = 0x20; // если файл не существует, выдать ошибку |
noreplace | = 0x40; // если файл существует, выдать ошибку |
binary | = 0x80; // открыть в двоичном режиме |
Открыть файл в программе можно с использованием конструктора, либо методом open. В open должны быть такие же параметры, как и в соответствующем конструкторе.
Пример 2
ifstream inpf(‘input.txt’, .ios::n| ios::nocreate); // использование конструктора
if(!inpf)
{
cout << “ невозможно открыть файл для чтения”;
return 1;
}
ofstream f;
fopen(“output.txt”); // использование метода open
if(!f)
{
cout<< “ невозможно открыть файл для записи ”;
return 1;
}
Чтение и запись выполняются с помощью операций чтения и извлечения аналогичных потоковым классам, либо с помощью методов класса.
Пример 3
#include<fstream.h>
int main()
{
char text[81], buf[81];
cout<<” введите имя файла “;
cin>>text;
ifstream(text, ios::in| ios::nocreate);
if(!f)
{
cout<<” ошибка открытия файла “;
return 1;
}
while(!f, eof())
{
f.getline(buf, 81);
cout<<buf<<endl;
}
return 0;
}
Для закрытия потока определен метод close, но поскольку он неявно выполняется деструктором, то явный вызов требуется, когда необходимо закрыть поток раньше конца его области видимости.
Строковые потоки
Строковые потоки позволяют считывать и записывать информацию из области оперативной памяти так же как из файла. консоли, экрана.
В стандартных библиотеках в потоковых потока определено три класса:
isstringstream – входной
ostringstream – выходной
stringstream – двунаправленный
Эти классы определяются в заголовочном файле <sstream>. Они являются производными от классов
istream,
ostream,
iostream. Соответственно наследуют << и >>, флаги форматирования,
манипуляторы.
Строковые классы определяются в соответствии с правилами
C++,
строковые потоки создаются с соответствующими участками памяти с помощью конструкторов.
explicit istringstream (int mode = ios::in);
explicit istringstream (const string &name, int mode = ios::in);
explicit ostringstream (int mode = ios::out);
explicit ostringstream (const string & name, int mode = ios::out);
explicit stringstream (int mode = ios::in | ios::out );
explicit stringstream (const strin &name, int mode = ios::in | ios::out);
Строковые потоки являются аналогами sprintf, sscanf, и могут использоваться для преобразования данных, когда они заносятся в некоторый участок памяти, а затем считываются в величины требуемых типов. Эти потоки могут применятся для обмена информацией между модулями программы.
В строковых потоках описан метод str, возвращающий копию строки или устанавливающий ее значение.
string str() const;
void str(const string &s);
Проверять строковый поток на переполнения не требуется, т.к. размер строки изменятся динамически.
Пример 4
Пример программы, в которой строковый поток используется для формирования сообщений, включающих текущее время и передает в качестве параметра.
#include<sstream>
#include<string>
#include<iostream>
#include<time>
using namespace std;
string message(int i;)
{
osting stream os;
time_t t;
time(&t);
os<<”time”<< ctime(&t) << “number:“ << i << endl;
return os.str();
}
int main()
{
cout << message (22);
return 0;
}
Потоки и типы, определяемые пользователем.
Чтобы вводить/выводить величины типов, определяемых пользователем требуется перегрузить эти операции. Это
бинарные операции, левым операндом которых является объект-поток, а правым – объект, который требуется извлечь/поместить в этот поток. Возвращенное значение должно быть ссылкой на поток, чтобы можно было организовать цепочки операций.
Пример 5
class b1
{
...
int x;
float y;
...
//Чтобы вводить/выводить объекты класса b1 требуется определить в классе b1 операции следующего вида
friends ostream &operator << (ostream out, b1 &c)
{
return out << “ x= “ << c.x << “ y= “ << c.y;
}
friend istream &operator >> (istream in, b1 &c)
{
cout << “Enter x:”; in>> c.x;
cout << “Enter y:”; in>> c.y;
return in;
}
...
};
После того как в программе определены операции ввода/вывода, в данной программе можно использовать
объекты класса b1 в операторах ввода/вывода наряду с величинами стандартных типов.
Пример 6
#include<iostream.h>
class b1
{
int x;
float y;
public:
b1(int nx = 1, float ny = 0.01)
{
x=nx;
y=ny;
}
friends ostream &operator << (ostream out, b1 &c)
{
return out << “ x= “ << c.x << “ y= “ << c.y;
}
friend istream &operator >> (istream in, b1 &c)
{
cout << “Enter x:”; in>> c.x;
cout << “Enter y:”; in>> c.y;
return in;
}
};
int main()
{
b1 c;
cout << c << endl;
b1 c1(100,100);
cout << c1 << endl;
b1 c2;
cin >> c2;
cout << c2 << endl;
return 0;
}