§17 Файлы. Текстовые файлы. Файловый ввод/вывод

Что такое файл?

После окончания работы программы и вывода результатов на дисплей данные будут утрачены безвозвратно, если они не будут записаны и сохранены в долговременной памяти. Для сохранения данных в долговременной памяти используются файлы. Файл (от англ. file) — это именованная область памяти на носителе информации. В программировании различают два типа файлов: текстовые и двоичные (бинарные). Вне зависимости от организации данных в файлах, данные в них представлены в двоичном формате, так что это деление условное. В текстовых файлах данные интерпретируются как последовательность символьных кодов. Специальные последовательности используются для указания признака конца строки. Это позволяет отвлечься от двоичного представления и рассматривать файл, как поток символов, равнозначный консольному потоку. Следовательно, для организации работы с текстовым файлом создаются потоки, аналогичные стандартным. Для работы с файлами в библиотеке ios существуют специальные классы файловых потоков ввода/вывода.

Файловые потоки

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

include <fstream>

В этом заголовочном файле определены три класса потоков:

  • ifstream – чтения файла (файловый ввод);
  • ofstream – записи в файл (файловый вывод);
  • fstream – чтение и запись

Если для стандартных потоков определены потоковые объекты cin и cout, то объекты файловых потоков разработчик программы должен создавать сам. В наших программах для файлового потока ввода будет использовано имя fin, а для потока вывода – fout.
Для создания объекта файлового потока ввода необходимо использовать имя класса и имя потока (конструктор), а также функцию open() связывающую потоковый объект с конкретным файлом:

ifstream fin;
fin.open("input");

Метод open() принимает в качестве аргумента строку – имя файла (input). В данном случае, предполагается, что файл находится в текущей директории проекта (иначе необходимо указать полный путь к файлу). Этот файл должен существовать и быть доступен для чтения в системе.
Код создания объекта fin можно сократить до следующего вида (при этом метод open() будет вызван неявно):

ifstream fin("input");

После создания объекта ввода, его можно использовать в программе точно так же, как и объект cin. Например:

fin >> a >> b >> c;

По завершении работы с файлом соединение с ним должно быть разорвано вызовом метода close():

fin.close();

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

ofstream fout;
fout.open("output");

или

ofstream fout("output");

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

fout << a << b << c << endl;

По завершении работы с файлом соединение с ним должно быть разорвано вызовом метода close():

fout.close();

Примечание. Метод close прекращает работу с файлом, но не уничтожает поток! Потоки fin и fout можно, впоследствии, вновь связать с этими или же с другими файлами.
По умолчанию файл открывается в текстовом режиме.
Рассмотрим пример простой программы. В исходном файле (input) записаны два числа, разделенные пробелом. Получить сумму этих двух чисел и записать сумму в выходном файле (output).
Программа 17.1

#include <fstream>
#include <iostream>
using namespace std;

int main() {
	int a, b;
	ifstream fin("input");
	ofstream fout("output");
	fin >> a >> b;
	cout << a << " " << b << endl;
	fout << a + b << endl;
	fin.close();
	fout.close();
	return 0;
}

Указатели чтения и записи

У каждого файлового объекта есть два ассоциированных с ним значения, называемые указатель чтения (get) и указатель записи (set). Эти значения определяют номер байта относительно начала файла, с которого будет производиться чтение или запись. Классы ifstream и ofstream имеют свои функции для перемещения указателя и определения его текущей позиции.

ifstream		ofstream
seekg(new_position)	seekp(new_position)
seekg(offset, from)	seekp(offset, from)
tellg()			tellp()

При чтении или записи в файл указатели перемещаются по файлу автоматически, но при необходимости указатели можно перемещать на заданную позицию. Методы seekg и seekp осуществляют смещение указателя от текущей позиции на величину new_position или offset. Аргументы new_position и offset (позиция файла и смещение от этой позиции) имеют машинно-зависимый целочисленный тип. Для offset он может быть как положительным, так и отрицательным (соответственно вперед и назад). Аргумент from - имеет три возможных значения:

  • ios::beg – отсчет смещения от начала
  • ios::cur – отсчет от текущей позиции
  • ios::end – отсчет от конца файла

Например:

  • fin.seekg(2, ios::beg); – 2 байта от начала
  • fin.seekg(-1, ios::cur); – назад на 1 байт
  • fin.seekg(0, ios::end); – перейти в конец файла

Если нужно получить текущее значение позиции указателя, то необходимо воспользоваться методами tellg() или tellp().
Метод eof() позволяет проверить находится ли указатель в конце файла. Данный метод возвращает 0, если конец файла ещё не достигнут и 1, в противном случае. Таким образом, можно применять eof() для осуществления построчного чтения текстового файла, например, в цикле while.

Режимы работы с файлом

Конструкторы ifstream и ofstream, а также метод open(), принимают, на самом деле, два аргумента. Второй аргумент определяет режим работы с файлом. Отсутствие второго аргумента означает, что второй аргумент задан не явно (по умолчанию). Режимы работы с файлом определяют каким образом нужно осуществлять работу с файлом. Ниже перечислены эти режимы:

  • ios::in – открыть файл для ввода (чтения). Может быть установлен только для объектов типа ifstream (режим по умолчанию) и fstream
  • ios::out – открыть файл для вывода (записи). Может быть установлен только для объектов типа ofstream (режим по умолчанию) и fstream
  • ios::ate – перейти в конец файла после открытия. Позиция указателя может быть изменена
  • ios::app – открыть файл для записи в конец файла. Может быть установлен, если не установлен режим trunc. Если режим app установлен, файл всегда открывается в режиме вывода (записи)
  • ios::trunc - усекает (удаляет из него всю информацию) файл, если он существует. Может быть установлен, только если устанавливается также режим out (когда используется конструктор fstream)
  • ios::binary – открыть в двоичном режиме

Битовая операция "|" служит для объединения нескольких режимов. В классе fstream режим по умолчанию не предусмотрен, поэтому при создании объектов данного класса режим нужно устанавливать явно.
Примеры использования:

  • ios::out | ios::app – открыть для записи с разрешением только на добавление
  • ios::in | ios::out – открыть для чтения и записи с разрешением на запись в произвольном месте файла
  • ios::in | ios::out | ios::trunc – открыть для чтения и записи с усечением существующего файла

Рассмотрим пример задачи в которой применяются как разные режимы, так и функции перемещения файлового указателя.
Программа 17.2 Записать в исходный файл input 10 строк в каждой из которых находится случайное число из отрезка [10, 99]. Получить из этого файла данные чтением построчно с суммированием и отправить среднее арифметическое в файл output. (Представить вещественное число в экспоненциальном формате).

#include <fstream>
#include <iostream>
#include <iomanip>
#include <ctime>
#include <random>
using namespace std;

int main() {
	default_random_engine rnd(time(0));
	uniform_int_distribution<unsigned> d(10, 99);
	int k = 0, j = 0;
	// открыть файл и для вывода, и для ввода
	fstream fin("input", ios::out | ios::in);
	ofstream fout("output");
	for (int i = 1; i <= 10; i++){
		k = d(rnd);
		fin << k << endl;
	}
	// вернем указатель в начало файла
	fin.seekg(0, ios::beg);
	while (!fin.eof()){
		fin >> k;
		j += k;
	}
	fout << scientific << float(j) / 10 << endl;
	fin.close();
	fout.close();
	return 0;
}


Print Friendly, PDF & Email

Comments are closed.