Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Объектно-ориентированное программирование на C+...doc
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
333 Кб
Скачать
☆

6. Потоки и классы ввода/вывода

Стандартные потоки

В С++ ввод и вывод осуществляется через потоки – объекты классов ввода/вывода, которые передают и принимают данные и связываются с физическими устройствами. Следующие потоки определены и автоматически открываются при запуске приложения:

extern istream cin; // cтандартный поток ввода с клавиатуры

extern ostream cout; // cтандартный поток вывода на экран

extern ostream cerr; /* cтандартный поток вывода сообщений об ошибках */

extern ostream clog;/*буферизованный поток вывода сообще­ний об ошибках */

Рассмотрим пример:

#include <iostream.h>//пример 60

int main() {

int a,b,c;

cout << "Hello, world\n";

cin>>a>>b>>c;

cout << "a/b+c=" << a*b-c << "\n";

cout << "a^b|c=" << (a^b|c) << "\n";

return 0; }

Операция << пишет аргумент, строку "Hello, world" в стандартный поток вывода cout. Ввод производится с помощью операции >> стандартного потока ввода cin. Приоритет операции вставки в поток << достаточно низок, чтобы не использовать скобки для арифметических выражений. Для операций с более низкими приоритетами без скобок не обойтись.

Форматирование

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

char* oct(long, int=0); // восьмеричное представление

char* dec(long, int=0); // десятичное представление

char* hex(long, int=0); // шестнадцатеричное представление

char* chr(int, int=0); // символ

char* str(char*, int=0); // строка

Второй (необязательный) па­раметр указывает, сколько символьных позиций должно использоваться. Если задано поле нулевой длины, то для вывода потребуется столько позиций, сколько нужно, иначе будет производиться усечение или дополнение. Например:

cout <<"dec("<< x << ")=oct("<< oct(x,6)<<")= hex("<< hex(x,4)<< ")";

Если x==15, то в результате получится:

dec(15) = oct( 17) = hex( f);

Для преобразования при выводе можно использовать строку в формате:

char* form(char* format, список параметров);

Аналогично стандартной функции printf() вывода языка C функция form() возвращает строку, получаемую в результате форматирования параметров, стоящих после первого управляющего параметра – строки формата format. Строка формата состоит из обычных символов, которые просто копируются в поток вывода, и спецификаций преобразования, влекущих преобразование и вывод параметра. Каждая спецификация преобразования начинается с символа %. Например: cout<<form(" х= %d ",х);

Манипуляторы

Манипуляторы – функции потока, которые можно вклю­чать в операции помещения и извлечения в потоки (<<, >>), они бывают следующие:

endl // помещение в выходной поток символа конца строки '\n'

ends // помещение в выходной поток символа '\0'

flush // вызов функции вывода буферизованных данных

dec, hex, oct // установка основания системы счисления

ws // игнорирование при вводе пробелов

setbase(int) // установка основания системы счисления 10,8,16

resetiosflasg(long) // сброс флагов форматирования по маске

setiosflags(long) // установка флагов форматирования по маске

setfill(int) // установка заполняющего символа

setprecision(int)/*установка точности вывода вещественных чисел */

setw(int) // установка ширины поля ввода-вывода

Пример вызова манипулятора:

cout << 15 << hex << 15 << setbase(8) << 15;

cout<<hex<<100<<oct<<100<<dec<<100;

Ошибки потоков

Каждый поток (istream или ostream) имеет ассоциированное с ним состояние, с помощью проверки которого осуществляется обработка ошибок и нестандартных условий.

Поток может находиться в одном из следующих состояний:

enum stream_state { _good, _eof, _fail, _bad };

Если состояние _good или _eof, последняя операция ввода прошла успешно, а если – _good, то следующая операция ввода может пройти успешно, в противном случае она закончится неудачей. Другими словами, применение операции ввода к потоку, который не находится в со­стоянии _good, является пустой операцией. Состояние потока можно проверять, например, так:

switch (cin.rdstate()) {

case _good: // последняя операция над cin прошла успешно

break;

case _eof: // конец файла

break;

case _fail: // некоего рода ошибка форматирования

break;

case _bad: // возможно, символы cin потеряны

break; }

Файловый ввод-вывод с применением потоков С++

В С++ существуют классы потоков ввода-вывода, определенные в соответствующей библио­теке:

ios //базовый потоковый класс

streambuf //буферизация потоков

istream //потоки ввода

ostream //потоки вывода

iostream //двунаправленные потоки

istrstream //строковые потоки ввода

ostrstream //строковые потоки вывода

strstream //двунаправленные строковые потоки

ifstream //файловые потоки ввода

ofstream //файловые потоки вывода

fstream //двунаправленные файловые потоки

Стандартные потоки (istream,ostream,iostream) служат для работы с терминалом. Строковые потоки (istrstream, ostrstream, strstream) – для ввода-вывода из строковых буферов, размещенных в памяти, файловые потоки (ifstream, ofstream, fstream) – для работы с файлами.

Для реализации файлового ввода-вывода небходимо включить заголовочный файл fstream.h, содержащий производные от istream и ostream классы ifstream, ofstream и fstream, и объявить соответствующие объекты. Например:

ifstream in;//ввод

ofstream out;//вывод

fstream io;//ввод - вывод

Файловые потоки можно определить с помощью конструкторов:

ofstream obj (filename, mode),

ifstream obj (filename, mode),

где mode может иметь следующие значения:

ios::app //запись в конец существующего файла

ios::ate //после открытия файла перейти в его конец

ios::binary /*открыть файл в двоичном режиме (по умолчанию – текстовый) */

ios::in //открыть для чтения

ios::nocreate //сообщать о невозможности открытия, если файл не существует

ios::noreplace //сообщать о невозможности открытия, если файл сущест­вует

ios::out //открыть для вывода

ios::trunc //если файл существует, стереть содержимое

При необходимости изменения способа открытия или применения файла можно при создании файлового потока использовать два или более флагов: ios::app|ios::noreplace

Для открытия файла одновременно для чтения и записи используются объекты класса fstream:

fstream obj(filename, ios::in|ios::app);

После объявления потоков открытие файла, связывающее его с потоком, можно производить не через конструктор, а с помощью метода open():

void open (char *filename,int mode,int access), где filename – имя файла, включающее путь; mode – режим открытия файла, access – доступ. Параметр аccess принимает следующие значения: 0 – файл со свободным доступом, 1 –только для чтения, 8 – архивный файл. Например:

ofstream out;

out.open(“test.dat”,ios::out,0);

Параметры можно задать по умолчанию.

оut.open(“test.dat”);//будет то же самое

При завершении программы открытые файлы неявно закрываются. Для явного закрытия объектов файловых потоков применяется метод close().

Ввод-вывод в файлы

Для чтения-записи в потоки можно использовать перегружаемые операторы-функции >> и <<. Например:

#include <fstream.h>//пример 61

int main(){

ofstream fout("test"); /* создание файла вывода c помощью конструктора */

if(!fout) { cout << "Файл открыть невозможно\n";

return 1; }

fout << "Привет!\n";

fout << 100 << ' ' << hex << 100 << endl;

fout.close();

ifstream fin("test"); // открытие обычного файла ввода

if(!fin) {cout << "Файл открыть невозможно\n";return 1;}

char str[80];

int i;

fin >> str >> i;

cout << str << ' ' << i << endl;

fin.close();

return 0; }

Сам оператор << можно перегрузить, как в следующем примере:

/*создание недружественной функции-вставки для объектов Coord */

#include <iostream.h>//пример 62

class Coord {

public:

int x,y;//должны быть открытыми

Coord() {x=0;y=0;}

Coord(int i,int j) {x=i;y=j;}

};

//функция вставки для объектов класса coord

ostream &operator<<(ostream &stream, Coord ob){

stream <<ob.x<<”,”<<ob.y<<’\n’;

return stream;

}

int main(){

Coord a(1,1), b(10,23);

cout <<a<<b;

return 0; }

Оператор ввода (извлечения) также можно перегрузить:

istream &operator>>(istream &stream, имя_класса ob){

//тело функций ввода

return stream;

}

При работе с файлами возможно использование методов ввода-вывода одного символа: istream &get(char &ch); и ostream &put(char ch);

Пример использования этих методов и аргументов командной строки:

#include <iostream.h>//пример 63

#include <fstream.h>

int main(int argc, char *argv[ ]) {

char ch;

if(argc!=2) {

cout << "Использование:WRITE<имя_файла>\n";

return 1;}

ofstream out(argv[1]);

if(!out) {cout << "Файл открыть невозможно\n";return 1; }

cout << "Для остановки введите символ $\n";

do {

cout << ": ";

cin.get(ch);

out.put(ch);

} while (ch!='$');

out.close();

return 0; }

Для записи-считывания блоков двоичных данных используются функции, которые считывают-записывают n байт в буфер или из буфера:

istream &read(unsigned char *buf, int n);

ostream &write(const unsigned char *buf, int n);

#include <iostream.h>//Пример 64

#include <fstream.h>

#include <string.h>

int main() {

ofstream out("test", ios::binary);/* не будет нежелательных проебразований символов при вводе и выводе*/

if(!out) {cout << "Файл открыть невозможно\n";return 1;}

double num = 100.45;

char str[ ] = "Это проверка";

out.write((char *) &num, sizeof(double));

out.write(str, strlen(str));

out.close();

return 0; }

Для позиционирования в файле имеются методы seekg(), seekp(), tellp(). При этом seekg() назначает или возвращает текущую позицию указателя чтения, а seekp() – те­кущую позицию указателя записи. Обе функции могут иметь один или два аргумента. При вызове с одним аргументом функции перемещают указа­тель на заданное место, а при вызове с двумя аргументами вычисляется от­носительная позиция от начала файла (ios::beg) текущей позиции (ios::cur) или от конца файла (ios::end). Текущая позиция определяется методом tellp().

Для объектов файловых потоков контроль состояния производится с помощью методов, манипулирующих флагами ошибок:

bad() – возвращает ненулевое значение, если обнаружена ошибка;

clear() – сбрасывает сообщения об ошибках;

eof() – возвращает ненулевое значение, если обнаружен конец файла;

fail() – возвращает ненулевое значение, если операция заверши­лась неудачно;

good() – возвращает ненулевое значение, если флаги ошибок не выставлены;

rdstate() – возвращает текущее состояние флагов ошибок.

Если флаги показывают наличие ошибки, все попытки поместить в поток новые объекты будут игнорироваться.