Тогда после выполнения оператора
in_stream.get(ch);
программа перейдет в состояние, изображенное на рис. 11.
Рис. 11. Состояние программы после чтения последнего символа из файла.
Теперь логическое выражение
in_stream.eof()
будет иметь истинное значение True. Если снова выполнить чтение символа:
in_stream.get(ch);
то в результате получится состояние, показанное на рис. 12.
Рис. 12. Состояние программы после операции чтения символа при установ-
ленном флаге EOF.
Ниже приведена простая программа для копирования текстового файла
"Lecture_4.txt" одновременно и на экран, и в другой файл "Copy_of_4.txt". Для
прекращения копирования применяется вызов функции "eof()". Обратите внимание
на цикл "while". Цикл с префиксным условием (предусловием) "while" является уп-
рощенным вариантом цикла "for". У цикла "while" в круглых скобках "()" нет опе-
раторов инициализации и изменения значений (подробно эти циклы рассматриваются
в следующей лекции).
#include <iostream.h>
#include <fstream.h>
int main()
{
char character;
ifstream in_stream;
ofstream out_stream;
47
in_stream.open( "Lecture_4.txt" );
out_stream.open( "Copy_of_4.txt" );
in_stream.get( character );
while ( !in_stream.eof() )
{
cout << character;
out_stream.put(character);
in_stream.get(character);
}
out_stream.close();
in_stream.close();
return 0;
}
Программа 5.1.
6. Передача потоков функциям в качестве параметров
Потоки можно использовать в качестве параметров функций, но их обязатель-
но надо передавать по ссылке (а не по значению). Ниже приведен усовершенствован-
ный вариант программы 5.1, в котором копирование выполняется специальной функ-
цией "copy_to(...)".
#include <iostream.h>
#include <fstream.h>
void copy_to( ifstream& in, ofstream& out );
// Главная функция
int main()
{
ifstream in_stream;
ofstream out_stream;
in_stream.open( "Lecture_4.txt" );
out_stream.open( "Copy_of_4.txt" );
copy_to( in_stream, out_stream );
out_stream.close();
in_stream.close();
return 0;
}
// Конец главной функции
// Функция для копирования файла в другой файл и на экран
void copy_to( ifstream& in, ofstream& out )
{
char character;
in.get( character );
while ( !in.eof() )
{
cout << character;
out.put( character );
48
in.get( character );
}
}
// Конец функции
Программа 6.1.
7. Операторы ввода/вывода ">>" и "<<"
До сих пор рассматривались способы записи и чтения из файлов отдельных
символов. На нижнем уровне, скрытом внутри классов ofstream and ifstream, объ-
екты этих классов всегда работают с файлами как с последовательностями символов.
Поэтому данные других типов ("int", "double" и др.) для записи в файл должны быть
преобразованы в последовательность символов. При чтении из файла эти последова-
тельности должны быть преобразованы обратно.
Некоторые преобразования типов данных автоматически выполняются опера-
торами ">>" и "<<" (в предыдущих лекциях они часто использовались для ввода с кла-
виатуры и вывода на экран). Например, из состояния, показанного на рис. 13:
Рис. 13. Состояние программы после открытия файла вывода (после подклю-
чения потока вывода к файлу).
с помощью оператора
out_stream << 437 << ' ';
программа перейдет в новое состояние, изображенное на рис. 14.
Рис. 14. Состояние программы после записи в поток вывода целого значения
"437" и пробела.
При использовании операторов ">>" и "<<" обязательно надо после каждого за-
писанного значения записывать еще как минимум один символ ' ' (пробел) или слу-
жебный символ '\n' (маркер конца строки). Это гарантирует, что элементы данных
будут корректно отделены в файле друг от друга, и их можно будет извлекать оттуда
с помощью оператора ">>". Например, в состоянии, показанном на рис. 15:
49
Рис. 15. Состояние программы в некоторый момент времени, когда текущая
позиция потока ввода установлена на пробел.
если "n" является переменной типа "int" и имеет значение 10, то выполнение опера-
тора
in_stream >> n;
приведет к состоянию, показанному на рис. 16.
Рис. 16. Состояние программы после чтения из потока ввода целого значения
"437".
Обратите внимание, что оператор ">>" перед числом 437 пропустил пробел ' '.
Этот оператор всегда пропускает пробелы, независимо от типа считываемых данных
(даже при чтении символов).
Работа с операторами ">>" и "<<" продемонстрирована в программе 7.1. Снача-
ла она создает файл "Integers.txt", записывает в него целые числа 51, 52, 53, 54 и
55, а затем считывает эти числа из файла.
#include <iostream.h>
#include <fstream.h>
int main()
{
char character;
int number = 51;
int count = 0;
ofstream out_stream;
ifstream in_stream1; // Поток для подсчета целых чисел.
ifstream in_stream2; // Поток для подсчета символов.
// Создание файла
out_stream.open( "Integers.txt" );
for ( count = 1; count <= 5; count++ )
out_stream << number++ << ' ';
out_stream.close();
// Подсчет количества целых чисел в файле
in_stream1.open( "Integers.txt" );
count = 0;
in_stream1 >> number;
while ( !in_stream1.eof() )
{
50
count++;
in_stream1 >> number;
}
in_stream1.close();
cout << "В файле хранится " << count << " целых чисел,\n";
// Подсчет количества символов, не являющихся разделителями
in_stream2.open( "Integers.txt" );
count = 0;
in_stream2 >> character;
while ( !in_stream2.eof() )
{
count++;
in_stream2 >> character;
}
in_stream2.close();
cout << "представленных с помощью " << count << " символов.\n";
return 0;
}
Программа 7.1.
Программа 7.1 выведет на экран следующие сообщения:
В файле хранится 5 целых чисел,
представленных с помощью 10 символов.
При подсчете символов в последней части программы 7.1 снова обратите вни-
мание на то, что, в отличие от функции "get(...)", оператор ">>" игнорирует в файле
пробелы (которые разделяют пять целых чисел).
8. Сводка результатов
В лекции рассмотрены способы работы с текстовыми файлами с помощью по-
токов ввода/вывода. "Низкоуровневый" ввод/вывод выполняется с помощью функций
"get(...)" и "put(...)", а "высокоуровневый" ввод/вывод значений разных типов –с
помощью потоковых операторов ">>" и "<<".
9. Упражнения
Упражнение 1
Напишите программу, печатающую на экране содержимое собственного ис-
ходного файла на Си++.
Упражнение 2
Разработайте программу, которая (1) начинается с оператора вывода тестового
сообщения:
cout << "Проверка: " << 16/2 << " = " << 4*2 << ".\n\n";
и затем (2) копирует собственный исходный файл на Си++ в файл
"WithoutComments.cpp" и на экран, при этом пропуская все комментарии между
маркерами "/* ... */" (и маркеры комментариев тоже).
51
Получившийся файл "WithoutComments.cpp" должен компилироваться и ра-
ботать точно так же, как и исходная программа.
Подсказки: (1) вам может пригодиться функция "putback()"; (2) для отслежива-
ния состояния, находитесь ли вы внутри комментария или нет, можете применить
логический "флаг".
Упражнение 3
Напишите программу, которая подсчитывает и выводит на экран количество
символов (включая пробелы) в собственном исходном файле.
Упражнение 4
Без использования массива (массивы будут рассмотрены в 6-й лекции) напи-
шите программу, которая печатает на экране собственный исходный файл в обратном
порядке.
Подсказки: (1) Для начальной части этой программы может пригодиться програм-
ма из упражнения 3. (2) Будьте внимательны и не используйте поток ввода после
ошибки – вместо этого работайте с новым потоком.
Примечание: не беспокойтесь, если перед началом печати происходит небольшая
задержка – открытие и закрытие файлов требует некоторого времени.
Упражнение 5
Что выведет на экран следующая программа?