// пропущена функция close
fs.open("foo", ios_base::out); // невыполнено: поток ifs уже открыт
if (!fs) error("невозможно");
Не забывайте проверять поток после его открытия.
Почему допускается явное использование функций open
close
? Дело в том, что иногда время жизни соединения с файлом не ограничивается его областью видимости. Однако это событие происходит так редко, что о нем можно не беспокоиться. Более важно то, что такой код можно встретить в программах, в которых используются стили и идиомы языков и библиотек, отличающихся от стилей и идиом, используемых в потоках iostream
(и в остальной части стандартной библиотеки C++).Как будет показано в главе 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 считывает данные
// из файла,