Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Tekhnologia_programmirovania.pdf
Скачиваний:
182
Добавлен:
08.04.2015
Размер:
1.76 Mб
Скачать

264

Глава 19. Ввод и вывод

Язык C++ не содержит встроенных средств ввода и вывода. Ввод и вывод обеспечивается стандартной библиотекой с заголовочным файлом iostream.h или iostream. При использовании iostream нужно использовать пространство имен std. В C++ можно применять все средства ввода и вывода языка Си, которые становятся доступными после включения в программу заголовочного файла stdio.h.

Средства ввода и вывода языка C++ реализованы в виде иерархии потоковых классов. Поток – это последовательность байтов. С точки зрения программы поток не зависит от тех конкретных устройств, с которыми ведется обмен данными. Поток может быть связан со стандартным устройством ввода (клавиатурой), стандартным устройством вывода (дисплеем), с файлом на диске или с массивом в оперативной памяти. Потоки бывают входными, выходными и двунаправленными, допускающими как чтение, так и запись. В составе библиотеки ввода и вывода предусмотрены средства для включения данных в поток и извлечения данных из потока.

Все потоковые классы имеют в качестве базового класс ios. Для непосредственной организации ввода и вывода используются классы:

istream

класс входных потоков;

ostream

класс выходных потоков.

iostream

класс двунаправленных потоков.

19.1.Вывод

Вклассе ostream есть функции-операторы << для вывода стандартных типов. В качестве результата данные функции возвращают ссылку на объект, для которого вызываются. Ниже приводится фрагмент объявления класса ostream.

class ostream: public ios

 

{

 

...

 

public:

 

ostream& operator<<(char*);

// Вывод строки

ostream& operator<< (int);

// Вывод целого

ostream& operator<< (long);

// Вывод длинного целого

ostream& operator<< (double);

// Вывод double

ostream& operator<< (long double);// Вывод long double

ostream& put(char);

// Вывод символа

Ввод и вывод 265

};

Реализация некоторых простых функций вывода выполнена непосредственно в классе, например, функция вывода целого int:

inline ostream& ostream::operator<< (int _i) { return *this << (long) _i; }

Видно, что int преобразуется к типу long, затем используется оператор для вывода long и возвращается поток *this, в который произведен вывод. Реализация оператора вывода long в классе не приводится, она содержится в заранее откомпилированной библиотеке.

В файле iostream объявлен объект cout класса ostream, связанный по умолчанию со стандартным выходным устройством (дисплеем), – стандартный выходной поток. Как уже говорилось, его можно перенаправить на файл средствами операционной системы.

Для ускорения выполнения операций ввода и вывода могут создаваться буфера в оперативной памяти, в которые предварительно помещаются выводимые или вводимые данные. В частности, поток cout имеет буфер.

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

Благодаря тому, что функция operator << возвращает ссылку на поток, можно писать цепочки вызовов вида:

double x=3.14;

cout << "\nx=" << x;

Последняя инструкции является сокращенной записью инструкции: cout.operator<<("\nx=").operator<<(x);

Первым вычисляется выражение cout.operator<<("\nx="). При его вычислении выводится строка "\nx=", значением этого выражения является cout. Затем в cout выводится x.

19.2.Ввод

Вклассе входных потоков istream определены функции-операторы

>>для ввода стандартных типов. Их объявления похожи на приведенные выше объявления функций вывода <<, только аргументы передаются в эти функции по ссылке, чтобы аргументам можно было присвоить значение внутри функции. Оператор >> ("взять из…") при чтении сначала пропускает все ведущие пробелы, затем извлекает

266 19

символы для вводимого значения до тех пока не встретит разделитель или недопустимый для вводимой величины символ. Разделителями данных при вводе являются пробелы.

Для организации ввода со стандартного входного устройства в iostream.h (iostream) объявлен объект cin класса istream, связанный по умолчанию с клавиатурой.

Функция-оператор >> возвращает ссылку на поток, для которого вызывается. Это позволяет строить цепочки вида:

cin >> i >> j >> k;

При наборе на клавиатуре 1 2 3 и нажатии клавиши Enter, переменные i, j, k получат соответствующие значения.

19.3. Ввод и вывод определяемых пользователем типов

Операторы вывода << и ввода >> можно переопределять для пользовательских типов. Эти операторы являются бинарными и требуют двух операндов. В стандартной библиотеке ввода и вывода предусмотрено, что их первым операндом является поток, поэтому, чтобы сохранить привычный способ их применения для пользовательских типов (классов), нужно при их перегрузке в качестве первого операнда также указывать поток, но в таком случае эти операторы не могут быть членами класса.

Программа 56. Перегрузка операторов ввода/вывода

// Файл UserIO.cpp #include <iostream.h>

class Point3{

// Точка в трехмерном пространстве

float x, y, z;

// Три координаты

friend ostream& operator<<(ostream&, Point3&);

// Оператор вывода

friend istream&

operator>>(istream& , Point3&);

// Оператор ввода

};

 

 

В простом классе Point3 функции-операторы >> и << объявлены как друзья, чтобы они имели доступ к закрытым членам этого класса. Далее следуют определения функций-операторов ввода и вывода.

// Оператор вывода для Point3

ostream& operator<<(ostream& outs, Point3& d)

{

// Вывод данных в поток outs

outs << "(" << d.x << ", " << d.y << ", " << d.z << ")";

 

Ввод и вывод 267

return outs;

// Возвращение потока

}

// Оператор ввода для Point3

istream& operator>>(istream& ins, Point3& d)

{

// Чтение данных из потока и возвращение потока return ins >> d.x >> d.y >> d.z;

}

Здесь использовано то, что стандартный оператор ввода >> возвращает поток, для которого вызывается, то есть ins. После того, как из этого потока будет произведено чтение, он возвращается из функции.

Теперь вводить и выводить данные типа Point3 можно так же, как данные стандартных типов:

void main()

 

{

 

Point3 F, G;

// Две точки с неопределенными координатами

cout << "\n Координаты точек после их создания:\nF = " << F << "\nG = " << G << endl;

cout << "Введите координаты двух точек: "; cin >> F >> G;

cout<<"Теперь координаты точек: F= " << F << ", G = " << G << endl;

}

Так как в классе Point3 нет конструктора, объекты F и G после создания будут иметь неопределенное значение. Программа выдает:

Координаты точек после их создания:

F= (9.455121e-41, 2.865269e-33, 1.023487e-16) G = (-1.370785e-29, 2.196764e-34, 0.000301) Введите координаты двух точек: 3 4 5 2 3 4

Теперь координаты точек: F= (3, 4, 5), G = (2, 3, 4)

19.4. Работа с файлами

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

ofstream – класс выходных файловых потоков; ifstream – класс входных файловых потоков;

fstream – класс двунаправленных файловых потоков.

Чтение из файла и запись в файл производятся с помощью объектов файловых потоковых классов (потоков), которые должны быть связаны с конкретными файлами на диске. Связь потока с файлом можно

268 19

установить с помощью функции-члена open, или с помощью конструктора, аргументами которых должно быть имя файла. Функция open и конструкторы потоковых классов могут иметь по два аргумента. Первым аргументом является имя открываемого файла, а второй аргумент задает режим работы с файлом. Для указания режима работы с файлом можно использовать константы, определенные в базовом классе

ios:

ios::out – файл используется для вывода; ios::in – файл используется для ввода; ios::app – добавления в конец файла.

Программа 57. Сравнение текстового и бинарного файлов

Впрограмме создаются два выходных файловых потока: текстовый

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

// Файл CmpTxtBn.cpp #include <stdlib> #include <fstream> #include <iostream> #include <windows> #include <time.h> #include <conio>

const int lenname = 13;

// Длина имени файла

using namespace std;

// Открываем стандартное пространство имен

char Buff[500];

// Буфер для преобразованной строки

char* Rus(char* in)

// Функция для преобразования русских букв

{

 

CharToOem(in, Buff);

// Функция CharToOem преобразует строку in

return Buff;

// в строку Buff, используя кодировку DOS

}

 

long FileSize(istream& f)

// Возвращает размер файла в байтах

{

// f – ссылка на поток

long k;

// Счетчик

 

Ввод и вывод 269

for (k = 0; !f.eof(); k++)

// Пока не достигнут конец файла,

f.get();

// читаем из него символ

f.clear();

// Сброс флагов ошибок потока

return k - 1;

 

}

В функции FileSize производится посимвольное чтение из файла, пока не будет достигнут его конец. Состояние «конец файла» не возникает после прочтения последнего символа, оно возникает после первой неудачной попытки прочитать из файла символ, поэтому возвращается не общее число k вызовов функции get, а значение k - 1, равное числу прочитанных из файла символов.

void main()

 

{

 

char* tf = "TxtFile.txt";

// Имя текстового файла

char* bf = "BinFile.bin";

// Имя бинарного файла

const int N =20;

// Количество чисел

ofstream tout(tf, ios::out);

// Создаем выходной текстовый поток

if(!tout){

// Проверяем открытие файла

cout<<Rus("\nНе могу создать файл") << tf;

cin.get();

 

exit(1);

 

}

ofstream bout(bf, ios::out | ios::binary);// Выходной бинарный поток

if(!bout){

 

// Проверка

cout<<Rus("\nНе могу создать файл") << bf;

cin.get();

 

 

exit(1);

 

 

}

 

 

int i, n;

 

 

randomize();

// Инициализация датчика случайных чисел

for(i = 0; i < N; i++){

 

n = rand();

 

// Генерируем числа

tout << ' ' << n;

 

// и заносим в текстовый

bout.write((char*)&n, sizeof(int));

// и бинарный файлы

}

 

 

tout.close();

 

// Закрываем

bout.close();

 

// потоки

ifstream tin(tf, ios::binary);

// Открываем оба файла на чтение

ifstream bin(bf, ios::binary);

// как бинарные

cout << Rus("\nРазмер текстового файла ") << tf

<< " = " << FileSize(tin);

 

cout << Rus("\nРазмер биарного файла ") << bf

<< " = " <<

FileSize(bin);

 

270

19

 

tin.close();

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

чисел,

 

 

tin.open(tf);

// окрываем снова как текстовый

if(!tin){

 

cout<<Rus("\nНе могу открыть файл ") << tf; cin.get();

exit(1);

}

// Вывод чисел из обоих файлов

cout << Rus("\nЧисла из файла ") << tf << endl;

while(tin.good()){

// Пока в потоке нет ошибок,

tin >> n;

// читаем число из файла

cout << n << ' ';

// и выводим на экран

if(wherex() > 60) // Если использовали более 60 позиций экрана,

cout << endl;

// переходим на новую строку

}

 

bin.seekg(0, ios::beg);

// Перемещаем текущий указатель файла

// к его началу cout << Rus("\nЧисла из файла ") << bf << endl; while(bin.good()){

bin.read((char*)&n, sizeof(int)); // Читаем байты числа

if(!bin.eof())

// Если после чтения не достигнут конец файла

cout << n << ' ';

// выводим число

if(wherex() > 60)

 

cout << endl;

 

}

 

tin.close();

// Закрываем

bin.close();

// потоки

cin.get();

 

}

 

Для проверки возможности чтения из потока использована функция bool good() const;

которая возвращает истину, если из потока возможно чтение. Эта функция более надежна, чем eof.

Функция int wherex();

возвращает текущую горизонтальную координату курсора. Курсор переводится на новую строку экрана, если текущая координата курсора больше 60.

Для записи в текстовый файл и чтения из него использованы обычные операторы вывода << и ввода >>. Для записи в бинарный файл

Ввод и вывод 271

использована функция write, для чтения – read. Подробнее о них см. §14.8.

Далее приводятся результаты, выводимые программой. В тестовом файле числа хранятся в виде последовательностей десятичных цифр, разделенных пробелами. Если открыть файл TxtFile.txt, то увидим в нем числа в виде одной длинной строки. Если открыть бинарный файл в текстовом редакторе, то увидим непрерывную последовательность непонятных значков.

Размер бинарного файла равен 80 байт, так как в него записано 20 целых чисел, каждое их которых занимает 4 байта. Размер тестового файла оказался больше, так как многие числа пятизначные, кроме того между числами ставятся пробелы.

Размер текстового файла TxtFile.txt = 117 Размер бинарного файла BinFile.bin = 81

Числа из файла TxtFile.txt

5100 5643 23190 21957 16631 26794 23086 2043 29278 17418 25104

17502 3733 13662 20114 25132 14391 11375 31451 27970

Числа из файла BinFile.bin

5100 5643 23190 21957 16631 26794 23086 2043 29278 17418 25104

17502 3733 13662 20114 25132 14391 11375 31451 27970

Задачи 19.4-19.4. Ввод и вывод

199.Для комплексных чисел (программа 54) перегрузите операторы ввода и вывода. Используйте эти операторы для ввода коэффициентов уравнения из файла и записи решения уравнения в файл.

200.Перегрузите операторы ввода и вывода для структуры Time (программа 41).

201.Для класса Date (программы 42-48) перегрузите операторы ввода и вывода.

202.В классе Polinom (задача ) перегрузите операторы ввода и вывода. Используйте их для ввода коэффициентов полинома из файла и записи результатов действий над полиномами в файл.