русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Двоичный ввод/вывод. Бинарные файлы

Рассматриваемые нами до этого времени примеры демонстрировали форматированный ввод/вывод информации в файлы. Форматированный файловый ввод/вывод чисел целесообразно использовать только при их небольшой величине и малом количестве, а также при необходимости обеспечения возможности просмотра файлов не программными средствами. В противном случае, конечно, гораздо эффективнее использовать двоичный ввод/вывод, при котором числа хранятся таким же образом, как в ОП компьютера, а не в виде символьных строк. Напомню, что целочисленное (int) или вещественное (float) значение занимает в памяти 4 байта, значение типа double – 8 байт, а символьное значение типа char - 1 байт. Например, число 12345 в текстовом (форматированном) файле занимает 5 байт, а в бинарном файле – 4 байта.

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

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

#include <iostream>

#include <fstream>

#include <conio>

using namespace std;

int main() {

int N, i;

cout << "Vvedite kol-vo elementov celochisl. massiva: "; cin >> N;

int *mas = new int [N];

for(i=0; i<N; i++) {

cout << " Vvedite " << i << "-i element: "; cin >> mas[i];

}

cout << "\nIdet zapis dannyh v fail..." << endl;

ofstream fout("c:\\os\\bin.dat", ios::binary);//созд. вых. бинарного потока

if(!fout) { cout << "\n Oshibka otkrytiya faila!"; getch(); return 1; }

fout.write(reinterpret_cast<char*>(mas), N*sizeof(int));//запись массива в файл

fout.close(); //закрытие потока

cout << "Dannye uspeshno zapisany!" << endl;

for(i=0; i<N; i++) mas[i]=0; //обнуление массива

ifstream fin("c:\\os\\bin.dat", ios::binary); //создание потока для чтения файла

if(!fin) { cout << "\n Oshibka otkrytiya faila!"; getch(); return 1; }

cout << "Fail sodergit:" << endl;

fin.read(reinterpret_cast<char*>(mas), N*sizeof(int)); //считывание массива из файла

for(i=0; i<N; i++) cout << mas[i] << " ";

getch(); return 0;

}

Особое внимание в данной программе надо уделить использованию функций write() (метод класса ofstream) и read() (метод класса ifstream). Эти функции думают о данных в терминах байтов и предназначены для переноса определённого количества байт из буфера данных в файл и обратно. Параметрами этих функций являются адрес буфера и его длина в байтах.

Функция write() предназначена для записи в файл указанного во втором параметре числа байт из указанного в первом параметре адреса буфера данных, а функция read() предназначена для считывания данных из файла. Здесь необходимо отметить, что эти функции работают с буфером данных только типа char. В связи с этим, в данной программе мы использовали оператор reinterpret_cast<>, который преобразует буфер наших данных типа int (mas) в буфер типа char.

Необходимо помнить, что приведение типа с помощью оператора reinterpret_cast<char*> необходимо только в тех случаях, когда первый параметр функций write() и read() не является символьным массивом (ведь символ типа char занимает только 1 байт). Кроме того, если необходимо записать или прочитать не массив, а отдельные переменные, то нужно использовать ссылочный механизм (ссылку на адрес буфера данных), например:

float cb;

ofstream fout(filename, ios::app | ios::binary);

fout.write(reinterpret_cast<char*>(&cb), sizeof(float));

Теперь необходимо обсудить второй параметр рассматриваемых функций. В данной программе, в качестве второго параметра мы использовали выражение N*sizeof(int), с помощью которого вычислили количество байт. Например, если у нас 5 целочисленных элементов массива, то число байт будет равно 20. Функция sizeof() возвращает количество байт, отводимое под указанный в качестве параметра тип данных. Например, sizeof(int) вернёт 4.

Итак, приведённая в этом примере программа позволяет записать в файл bin.dat данные в бинарном виде и считывать их из этого бинарного файла. Причём после считывания эти данные приводятся к типу int, приобретают структуру массива и с ними можно производить любые операции.

Теперь, представим себе, что необходимо написать программу позволяющую считывать данные из файла bin.dat, причём мы знаем только то, что в данном файле записаны элементы целочисленного массива в бинарном виде. Количество записанных элементов (N) нам не известно. При создании программы мы не имеем права использовать константный массив, т.е. выделять память под него на этапе создания программы. Это приведет к ошибочному результату. Поскольку слишком малое значение N приведёт к тому, что считаются не все элементы массива, а слишком большое значение N приведёт к заполнению лишних ячеек случайными значениями.

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

#include <iostream>

#include <fstream>

#include <conio>

using namespace std;

int main() {

int N, i, sum=0, dfb; //dfb - длина файла в байтах

ifstream fin("c:\\os\\bin.dat", ios::binary);

if(!fin) { cout << "Oshibka otkrytiya faila!"; getch(); return 1; }

fin.seekg(0, ios::end); //устанавливаем позицию чтения на конец файла (от конца 0 байт)

dfb = fin.tellg(); //получаем значение позиции конца файла (в байтах)

N=dfb/4; //зная, что целое число занимает 4 байта, вычисляем кол-во чисел

int *arr = new int [N]; //создаём динамический массив

fin.seekg(0, ios::beg); //перед чтением данных, перемещаем текущую позицию на начало файла

fin.read(reinterpret_cast<char*>(arr), dfb);

cout << "Iz faila schitano " << N << " elementov:" << endl;

for(i=0; i<N; i++) cout << arr[i] << " ";

for(i=0; i<N; i++) sum=sum+arr[i];

cout << "\n Ih summa = " << sum;

getch(); return 0;

}

Рассмотрим детально данную программу, в которой мы активно использовали функции seekg() и tellg(), являющиеся методами класса ifstream. Здесь необходимо отметить, что с любым файлом при его открытии связывается так называемая текущая позиция чтения или записи. Когда файл открывается для чтения, эта позиция по умолчанию устанавливается на начало файла. Но достаточно часто бывает нужно контролировать позицию вручную, чтобы иметь возможность читать и писать, начиная с произвольного места файла. Функции seekg() и tellg() позволяют устанавливать и проверять текущий указатель чтения, а функции seekp() и tellp() – выполнять те же действия для указателя записи.

Метод seekg(1_параметр, 2_параметр) перемещает текущую позицию чтения из файла на указанное в 1_параметре число байт относительно указанного во 2_параметре места. 2_параметр может принимать одно из трёх значений:

ios::beg – от начала файла;

ios::cur – от текущей позиции;

ios::end – от конца файла.

Здесь beg, cur и end – являются константами, определёнными в классе ios, а символы :: означают операцию доступа к этому классу. Например, оператор fin.seekg(-10, ios::end); позволяет установить текущую позицию чтения из файла за 10 байтов до конца файла.

Теперь вернёмся к описанию работы программы. Исходя из того, что нам не известно количество чисел записанных в файл, вначале необходимо узнать число чисел. Для этого, с помощью fin.seekg(0, ios::end); мы перемещаемся в конец файла и посредством функции tellg() возвращаем в переменную dfb длину файла в байтах. Функция tellg() возвращает текущую позицию указателя в байтах. Так как длина одного целого числа в байтах нам известна (4 байта), нетрудно вычислить количество записанных в файл чисел, зная длину файла в байтах (N=dfb/4;). Узнав количество чисел, создаём динамический массив и перемещаемся в начало файла для того, чтобы начать считывание данных с помощью функции read(). После того, как указанное нами число байт данных (dfb) перенесено в буфер данных (arr), считанные таким образом данные приобретают структуру массива и становятся полностью пригодны для каких укодно операций и преобразований.

Просмотров: 2410


Вернуться в оглавление



Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.