Шаг 495.
Библиотека STL.
Ввод-вывод с использованием потоковых классов. Режимы открытия файлов

    На этом шаге мы перечислим режимы открытия файлов и рассмотрим особенности их использования.

    В таблице 1 перечислены флаги управления режимами открытия файлов, определенные в классе ios_base. Флаги относятся к типу openmode и группируются в битовые маски по аналогии с флагами fmtflags.

Таблица 1. Флаги открытия файлов
Флаг Описание
in Открытие файла для чтения (используется по умолчанию для ifstream)
out Открытие файла для записи (используется по умолчанию для ofstream)
арр Запись данных производится только в конец файла
ate Позиционирование в конец файла после открытия ("at end")
trunc Удаление старого содержимого файла
binary Специальные символы не заменяются

    Флаг binary запрещает преобразование специальных символов или символьных последовательностей (например, конца строки или конца файла). В операционных системах типа MS-DOS или OS/2 конец логической строки в тексте обозначается двумя символами (CR и LF). При открытии файла в обычном текстовом режиме (сброшенный флаг binary) символы новой строки заменяются последовательностью из двух символов, и наоборот. При открытии файла в двоичном режиме (с установленным флагом binary) эти преобразования не выполняются.

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

    В некоторых реализациях имеются дополнительные флаги типа nocreate (файл должен существовать при открытии) и noreplace (файл не должен существовать). Однако эти флаги отсутствуют в стандарте, поэтому их использование влияет на переносимость программы.

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

  std::ofstream file("xyz.out", std::ios::out | std::ios::app);

    В таблице 2 представлены различные комбинации флагов и их аналоги - строковые обозначения режимов, используемые функцией открытия файлов fopen() в интерфейсе языка С. Комбинации с флагами binary и ate не приводятся. Установленный флаг binary соответствует строке с присоединенным символом b, а установленный флаг ate соответствует позиционированию в конец файла немедленно после открытия. Другие комбинации, отсутствующие в таблице (например, trunc|app), недопустимы.

Таблица 2. Описание режимов открытия файлов в C++
Флаги ios_base Описание Обозначения режимов в С
in Чтение (файл должен существовать) "r"
out Стирание и запись (файл создается при необходимости) "w"
out|trunc Стирание и запись (файл создается при необходимости) "w"
out|app Присоединение (файл создается при необходимости) "a"
in|out Чтение и запись с исходным позиционированием в начало файла "r+"
in|out|trunc Стирание, чтение и запись (файл создается при необходимости) "w+"

    Открытие файла для чтения и/или записи не зависит от класса соответствующего объекта потока данных. Класс лишь определяет режим открытия по умолчанию при отсутствии второго аргумента. Это означает, что файлы, используемые только классом ifstream или ofstream, могут открываться для чтения и записи. Режим открытия передается соответствующему классу потокового буфера, который открывает файл. Тем не менее операции, разрешенные для данного объекта, определяются классом потока данных.

    Также существуют три функции для открытия и закрытия файлов, принадлежащих файловым потокам данных (таблица 3).

Таблица 3. Функции открытия и закрытия файлов
Функция Описание
ореn(имя) Открытие файла для потока в режиме по умолчанию
ореn(имя, флаги) Открытие файла для потока в режиме, определяемом переданными флагами
close() Закрытие файлового потока
is_open() Проверка открытия файла

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

//---------------------------------------------------------------------------

#include <vcl.h>
// Заголовочные файлы для файлового ввода-вывода
#include <fstream>
#include <iostream>

#include <conio.h>   //необходимо для getch()

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
using namespace std;

std::string ToRus(const std::string &in)
{
  char *buff = new char [in.length()+1];
  CharToOem(in.c_str(),buff);
  std::string out(buff);
  delete [] buff;
  return out;
}

// Для всех файлов, имена которых переданы в аргументах командной строки,
// - открыть, вывести содержимое и закрыть файл

int main (int argc, char* argv[])
{
  ifstream file;

  // Перебор аргументов командной строки
  for (int i=1; i<argc; ++i) {

      // Открытие файла
      file.open(argv[i]);

      // Вывод содержимого файла в cout
      char c;
      while (file.get(c)) {
          cout.put(c);
      }

      // Сброс флагов eofbit и failbit, установленных
      // при обнаружении конца файла
      file.clear();

      // Закрытие файла
      file.close();
  }
  cout << ToRus("Работа выполнена");

  getch();
  return 0;
}

//---------------------------------------------------------------------------
Текст этого примера можно взять здесь.

    Результат работы программы выглядит так:


Рис.1. Результат работы приложения

    Обратите внимание: после завершения обработки файла вызывается функция clear() для сброса флагов состояния, установленных при обнаружении конца файла. Этот вызов необходим, поскольку потоковый объект используется для нескольких файлов. Функция ореn() никогда не сбрасывает флаги состояния. Следовательно, если поток данных не находится в нормальном состоянии, после его закрытия и повторного открытия вам все равно придется вызвать clear(), чтобы сбросить установленные флаги. То же самое следует сделать и при открытии другого файла.

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

  // Запись содержимого файла в cout 
  std::cout << file.rdbuf();

    На следующем шаге мы рассмотрим произвольный доступ к файлам.




Предыдущий шаг Содержание Следующий шаг