ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 12) онлайн

Двоичный ввод/вывод

Форматированный файловый ввод/вывод чисел целесообразно использовать

только при их небольшой величине и малом количестве. В противном случае,

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

числа хранятся таким же образом, как в ОП компьютера, а не в виде символьных

строк. Целочисленные значения занимают 4 байта, тогда как текстовая версия

числа, например «12345», занимает 5 байтов. Значения типа float также всегда

занимают 4 байта. А форматированная версия «6,02314е13» занимает 10 байтов.

В следующем примере показано, как в бинарном виде массив целых чисел запи-

сывается в файл и читается из него. При этом используются две функции — write()

(метод класса ofstream), а также read() (метод ifstream). Эти функции думают о дан-

ных в терминах байтов (тип char). Им все равно, как организованы данные, что они

собой представляют, — они просто переносят байты из буфера в файл и обратно.

Параметрами этих функций являются адрес буфера и его длина. Адрес должен

быть вычислен с использованием reinterpret_cast относительно типа char*. Вторым

параметром является длина в байтах (а не число элементов данных в буфере).

Листинг 12.9. Программа BINIO

// binio.cpp

// Двоичный ввод/вывод целочисленных данных

#include <fstream>         // для файловых потоков

#include <iostream>

using namespace std;

const int MAX = 100;       // размер буфера

int buff[MAX];             // буфер для целых чисел

 

int main()

{

  for(int j=0; j<MAX; j++) // заполнить буфер данными

    buff[j] = j;           // (0, 1, 2, ...)

 

Листинг 12.9 (продолжение)

                           // создать выходной поток

  ofstream os("edata.dat", ios::binary);

                           // записать в него

  os.write(reinterpret_cast<char*>(buff), MAX*sizeof(int) );

  os.close();              // должен закрыть его

 

  for(j=0; j<MAX; j++)     // стереть буфер

    buff[j] = 0;

                           // создать входной поток

  ifstream is("edata.dat", ios::binary);

                           // читать из него

  is.read( reinterpret_cast<char*>(buff), MAX*sizeof(int) );

 

  for(j=0; j<MAX; j++)     // проверка данных

    if( buff[j] != j )

      { cerr << "Некорректные данные!\n"; return 1; }

  cout << "Данные корректны\n";

  return 0;

}

При работе с бинарными данными в качестве второго параметра write() и read()

следует использовать ios::binary. Это необходимо по той причине, что текстовый

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

с данными. Например, специальный символ '\n' занимает два байта (на самом

деле это и есть два действия — перевод каретки и перевод строки). Это делает

текст более удобным для чтения в DOS утилитами типа TYPE, но для бинарных

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

иметь ASCII-код 10, переводится двумя байтами. Аргумент ios::binary — типичный

пример бита состояния. Мы еще будем говорить об этом при обсуждении функ-

ции ореn() немного позднее в этой главе.

 

22