Как будет показано в главе 11, о файлах можно сказать намного больше, но сейчас нам достаточно того, что их можно использовать в качестве источников и адресатов данных. Это позволяет нам писать программы, которые были бы нереалистичными, если бы предложили пользователю непосредственно вводить с клавиатуры всю входную информацию. С точки зрения программиста большое преимущество файла заключается в том, что мы можем снова прочитать его в процессе отладки, пока программа не заработает правильно.
10.5. Чтение и запись файла
Посмотрим, как можно было бы считать результаты некоторых измерений из файла и представить их в памяти. Допустим, в файле записана температура воздуха, измеренная на метеостанции.
0 60.7
1 60.6
2 60.3
3 59.22
...
Этот файл содержит последовательность пар (час, температура). Часы пронумерованы от 0
до 23
, а температура измерена по шкале Фаренгейта. Дальнейшее форматирование не предусмотрено; иначе говоря, файл не содержит никаких заголовков (например, информации об источнике данных), единиц измерений, знаков пунктуации (например, скобок вокруг каждой пары значений) или признак конца файла. Это простейший вариант.
Представим информацию в виде структуры Reading
.
struct Reading { // данные о температуре воздуха
int hour; // часы после полуночи [0:23]
double temperature; // по Фаренгейту
Reading(int h, double t) :hour(h), temperature(t) { }
};
В таком случае данные можно считать следующим образом:
vector
int hour;
double temperature;
while (ist >> hour >> temperature) {
if (hour < 0 || 23
temps.push_back(Reading(hour,temperature));
}
Это типичный цикл ввода. Поток istream
с именем ist
мог бы быть файловым потоком ввода (ifstream
), как в предыдущем разделе, стандартным потоком ввода (cin
) или любым другим потоком istream
. Для кода, подобного приведенному выше, не имеет значения, откуда поток istream
получает данные. Все, что требуется знать нашей программе, — это то, что поток ist
относится к классу istream
и что данные имеют ожидаемый формат. Следующий раздел посвящен интересному вопросу: как выявлять ошибки в наборе входных данных и что можно сделать после выявления ошибки форматирования.
Записать данные в файл обычно проще, чем считать их оттуда. Как и прежде, как только поток проинициализирован, мы не обязаны знать, что именно он собой представляет. В частности, мы можем использовать выходной файловый поток (ofstream
) из предыдущего раздела наравне с любым другим потоком ostream
.
Например, мы могли бы пожелать, чтобы на выходе каждая пара была заключена в скобки.
for (int i=0; i
ost << '(' << temps[i].hour << ',' << temps[i].temperature << ")\n";
Затем итоговая программа прочитала бы исходные данные из файла и создала новый файл в формате (час, температура).
#include "std_lib_facilities.h"
struct Reading { // данные о температуре воздуха
int hour; // часы после полуночи [0:23]
double temperature; // по Фаренгейту
Reading(int h, double t):hour(h), temperature(t) { }
};
int main()
{
cout << "Пожалуйста, введите имя файла для ввода: ";
string name;
cin >> name;
ifstream ist(name.c_str()); // поток ist считывает данные
// из файла,
// имя которого задано строкой name
if (!ist) error("Невозможно открыть файл для ввода ",name);
cout << "Пожалуйста, введите имя файла для вывода: ";
cin >> name;
ofstream ost(name.c_str()); // поток ost записывает данные
// в файл, имя которого задано
// строкой name
if (!ost) error("Невозможно открыть файл для вывода ",name);
vector
int hour;