Скачиваний:
190
Добавлен:
05.07.2021
Размер:
16.53 Mб
Скачать
  1. Проверка состояния файлового потока. Метод is_open(). Особенности открытия нескольких файлов. Пример их использования.

Новые реализации C++ предлагают способ проверки того, открыт ли файл — метод is_open().

if (!fin.is_open()){ //попытка открытия не удалась

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

В более ранних версиях проверка успешности открытия файла выполнялась следующим образом:

if(fin.fail()) //неудача открытия if(!fin.good()) //неудача открытия if(!fin) //неудача открытия

Объект fin, когда он используется в условии if, преобразуется в false, если fin.good() возвращает false, и в true — в остальных случаях, поэтому две приведенные формы эквивалентны. Однако эти тесты не могут правильно распознать ситуацию, когда предпринимается попытка открытия файла с неподходящим режимом файла. Метод is_open() перехватывает ошибки подобного рода, наряду с теми,которые перехватываются методом good(). Однако в старых реализациях C++ метод is_open() отсутствует.

ОТКРЫТИЕ НЕСКОЛЬКИХ ФАЙЛОВ

Если требуется, чтобы два файла были открыты одновременно, то нужно создать отдельный поток для каждого файла. Например, программа, которая сравнивает два отсортированных файла и отправляет результат в третий, должна создать два объекта ifstream для двух входных файлов и один объект ofstream — для выходного файла. Количество файлов, которые можно открыть одновременно зависит от операционной системы.

Однако можно запланировать последовательную обработку файлов. В этом случае можно открыть единственный поток и по очереди ассоциировать его с каждым из этих файлов.

ifstream fin; //создание потока конструктором по умолчанию fin.open("file1.txt");//ассоциирование потока с файлом file1.txt

//выполнение действий с объектом fin

fin.close(); //разрыв связи потока с файлом file1.txt fin.clear();//сброс fin (может не требоваться)

fin.open("file2.txt");//ассоциирование потока с файлом file2.txt

  1. Режимы открытия файла (ios_base::арр, ios_base::ate, ios_base::binary, ios_base::in, ios_base::out, ios_base::trunc). Метод open(). Связь между режимами открытия файлов языков C++ и С. Добавление данных к содержимому файла. Использование текстового и бинарного файлового ввода-вывода. Примеры их использования.

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

ifstream fin("file1", ios_base::in);//конструктор с аргументом режима ofstream fout;

fout.open("file2", ios_base::out);//open() с аргументом режима

Класс ios_base определяет тип openmode, представляющий режим взаимодействия с потоком.

Таблица – Режимы открытия файлов C++ и С

Режим C++

Режим С

Описание

ios_base::in

"r"

Открыть для чтения

ios_base::out

"w"

To же, что и ios_base::out | ios_base::trunc

ios_base::out |

ios_base::trunc

"w"

Открыть для записи с усечением

существующего файла

ios_base::out | ios_base::app

"a"

Открыть для записи с разрешением только на добавление

ios_base::in | ios_base::out

"r+"

Открыть для чтения и записи с разрешением на запись в произвольном месте файла

ios_base::in | ios_base::out | ios_base::trunc

"w+"

Открыть для чтения и записи с усечением существующего файла

c++mode | ios_base::binary

"cmodeb"

Открыть в режиме c++mode или соответствующем режиме cmode и в бинарном (нетекстовом) режиме. Например, ios_base::in | ios_base::binary становится "rb"

c++mode | ios_base::ate

"cmode"

Открыть в указанном режиме и перейти к концу файла. В С вместо кода режима используется

отдельный вызов функции. Например, ios_base::in | ios_base::ate преобразуется в режим "r" с последующим вызовом функции

fseek(file, 0, SEEK_END)

ofstream fout("data", ios_base::out | ios_base::app);

Этот код использует операцию | для объединения режимов. Поэтому ios_base::out | ios_base::арр означает, что нужно включить и режим out, и режим арр.

Чтобы сохранить данные в бинарной форме вместо текстовой, можно воспользоваться методом write(). Этот метод копирует указанное количество байт из памяти в файл. Он будет побайтно копировать данные любого типа без каких-либо преобразований. Например, если передать ему адрес переменной типа long и указать, что необходимо скопировать 4 байта, он буквально скопирует в файл 4 байта, составляющие значение типа long, не преобразуя его в текст. Единственной особенностью использования метода write() является то, что необходимо использовать приведение адреса к типу указателя на значение типа char. Тот же подход можно использовать для копирования всей структуры Planet.

Чтобы получить количество байт, которые должны быть записаны, следует применить операцию sizeof:

fout.write((char*)& planet, sizeof planet);

Этот оператор вынуждает программу обратиться к адресу структуры planet и скопировать 40 байт (значение выражения sizeof planet), начиная с указанного адреса, в файл, подключенный к fout.

Чтобы восстановить информацию из файла, нужно использовать соответствующий метод read() с объектом ifstream:

ifstream fin("planets.dat", ios_base::in | ios_base::binary);

fin.read((char*)& planet, sizeof planet);

  1. Последовательный и произвольный доступ. Методы seekg(), seekp(), seekg(), seekp() (перегрузка, особенности использования). Методы tellg(), tellp(). Использование произвольного доступу к содержимому бинарного файла. Примеры использования последовательного и произвольного доступа.

Произвольный доступ (random access) означает возможность произвольного перемещения в любую позицию в файле вместо последовательного перемещения по нему.

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

finout.open(file,ios_base::in|ios_base::out | ios_base::binary);

Далее понадобится способ перемещения по файлу. Класс fstream наследует два предназначенных для этого метода: seekg() перемещает в заданную позицию файла указатель ввода, a seekp() перемещает указатель вывода. На самом деле, поскольку класс fstream использует буферы для промежуточного хранения данных, эти указатели указывают на положение в буферах, а не в реальном файле.

Можно также применять метод seekg() с объектом ifstream, a seekp() — с объектом ofstream.

Методы seekg() и seekp() являются шаблонными. В этой теме используется специализация шаблона для типа char. Для такой специализации приведенные прототипы метода seekg() эквивалентны следующим:

istream& seekg(off_type off, ios_base::seekdir way); istream& seekg(pos_type pos);

ПОВЫШЕНИЕ ТИПА

В более ранних версиях C++ методы seekg() были значительно проще. Типы streamoff и streampos были определены как typedef для некоторого целочисленного типа, такого как long.

следующий оператор устанавливает указатель файла на 112-й байт, который в файле является 113-м байтом:

fin.seekg(112);

Если требуется проверить текущую позицию файлового указателя, то можно воспользоваться методом tellg() для входных потоков и методом tellp() — для выходных.

//Пример №5. Использование произвольного доступу к содержимому бинарного файла

#include <iostream>

#include <fstream>

#include <iomanip>

#include <cstdlib> //для exit()

const int LIM = 20; struct planet {

char name[LIM]; //название планеты double population; //население double g; //ускорение свободного падения

};

using namespace std; const char* file = "planets.dat";

inline void eatline() { while (cin.get() != '\n') continue; } int main() { system("chcp 1251"); system("cls"); planet pl; cout << fixed;

//Отображение начального содержимого файла

fstream finout; //потоки чтения и записи

finout.open(file, ios_base::in | ios_base::out | ios_base::binary); int ct = 0; if (finout.is_open()) {

finout.seekg(0); //перейти в начало

cout << "Начальное содержимое файла " << file << " file: \n";

while (finout.read((char*)& pl, sizeof pl)) { cout <<ct++<< " : "<< setw(LIM)<<pl.name << ": " <<setprecision(0)<<setw(12) << pl.population

<<setprecision(2)<< setw(6) << pl.g << endl;

}

if (finout.eof()) finout.clear(); //очистить флаг eof

else {

cerr << "Ошибка чтения данных " << file << ".\n";

exit(EXIT_FAILURE);

}

}

else {

cerr << file << " не может быть открыт!\n"; exit(EXIT_FAILURE);

}

//Изменить запись

cout<<"Введите номер записи, которую вы хотите изменить: "; long rec;

cin >> rec;

eatline(); //избавление от символов новой строки if (rec < 0 || rec >= ct) { cerr << "Неверный номер записи!\n"; exit(EXIT_FAILURE);

}

streampos place = rec * sizeof pl; //преобразование в тип streampos

finout.seekg(place); //произвольный доступ if (finout.fail()) {

cerr << "Ошибка при попытке перемещения по файлу\n"; exit(EXIT_FAILURE);

}

finout.read((char*)& pl, sizeof pl);

cout << "Ваш выбор:\n";

cout << rec << ": " << setw(LIM) << pl.name << ": " << setprecision(0) << setw(12) << pl.population << setprecision(2) << setw(6) << pl.g << endl; if (finout.eof())

finout.clear(); //очистить флаг eof cout << "Введите имя планеты: "; cin.get(pl.name, LIM); eatline();

cout << "Введите количество населения: "; cin >> pl.population;

cout << "Введите ускорения свободного падения: "; cin >> pl.g;

finout.seekp(place); //вернуться назад finout.write((char*)& pl, sizeof pl) << flush; if (finout.fail()) {

cerr << "Ошибка при попытке записи\n"; exit(EXIT_FAILURE);

}

//Отображение измененного файла ct = 0;

finout.seekg(0); //перейти в начало файла cout << "Новое содержимое файла " << file << " :\n";

while (finout.read((char*)& pl, sizeof pl)) { cout << ct++ << ": " << setw(LIM) << pl.name << ": " << setprecision(0) << setw(12) << pl.population

<< setprecision(2) << setw(6) << pl.g << endl;

}

finout.close();

cout << "Программа выполнена успешно.";

return 0;