Текстовые и бинарные потоки их организация и отличия записи данных.
Знакомство с вводом-выводом обычно начинается с текстовых файлов (см. 4.5). У многих оно этим и заканчивается. Однако существует еще одна общая форма представления данных в файлах, на самом деле более многообразная – двоичный файл. Сюда относятся не только файлы с «мультимедийным» содержанием: документы, изображение, звук. Форматы двоичных файлов используются в базах данных и в самой файловой системе. Ведь файловая система – это не что иное, как большой двоичный файл, в котором построена сложная многоуровневая динамическая структура данных, включающая в себя все файлы каталоги.
Модель двоичного файла
Двоичный файл отличается от текстового тем, что данные в нем представлены во внутренней форме (см.1.3). А поскольку при внутреннем представлении используется двоичная система счисления, то «в честь ее» файлы и называются двоичными. По существу, двоичный файл является аналогом внутренней (оперативной, физической) памяти – неограниченным массивом байтовс возможностью непосредственного обращения (произвольного доступа) к любой его части.
Такая модель файла полностью совпадает с системой представлений, принятой в Си для работы с памятью на низком (физическом уровне).
· физическая память имеет байтную структуру – единицей адресации является байт;
· любая переменная занимает фиксированное количество байтов, определяемое ее типом. Операция sizeofвозвращает эту размерность;
· указатель на переменную интерпретируется как ее адрес в памяти. Преобразование типа указателя к void* позволяет интерпретировать его как «чистый» адрес, а преобразование к char* - как указатель на массив байтов (физическое представление памяти).
Исходя из этих принципов, функции двоичного ввода-вывода fread и fwrite переносят содержимое памяти в двоичный файл «прозрачно», т.е. байт в байт без каких либо преобразований. Функции используются для перенесения данных из файла в память программы (чтение) и обратно (запись).
int fread (void *buf, int size, int nrec, FILE *fd);
int fwrite (void *buf, int size, int nrec, FILE *fd);
Особенностью этих функций является то, что для них безразличен (неизвестен) характер структуры данных в той области памяти, в которую осуществляется ввод-вывод (указатель void* buf). Функци freadчитает, а функция fwrite пишет в файл, начиная с текущей позиции, массив из nrec элементов размерностью size байтов каждый, возвращая количество успешно прочитанных (записанных) элементов.
Чтобы воспользоваться этими функциями, необходимо обеспечить преобразования переменных к «массиву байтов», используя указатели для задания адресов и операцию sizeof для вычисления размерности:
// Прочитать целую переменную и следующий за ней
// динамический массив из n переменных типа double
int n; // в целой переменной – размерность массива
fread(&n, sizeof(int),1,fd); // указатель на переменную int
double *pd = new double[n];
fread(pd, sizeof(double),n,fd); // преобразование к void* - неявное
В принципе, бинарные данные обслуживаются наподобие текстовых. Отличие состоит в том, что если бинарные данные записываются в определенной логической структуре, то они должны считываться из файла в переменную того же структурного типа.
Первый параметр методов write и read (адрес блока записи/чтения) должен иметь тип символьного указателя char *, поэтому необходимо произвести явное преобразование типа адреса структуры void *. Второй параметр указывает, что бинарные блоки файла имеют постоянный размер байтов независимо от фактической длины записи. Следующее приложение дает пример создания и отображения данных простейшей записной книжки. Затем записи файла последовательно считываются и отображаются на консоли.
#include <iostream>
#include <fstream>
#include <locale>
using namespace std;
struct Notes { // структура данных записной книжки
char Name[60]; // Ф.И.О.
char Phone[16]; // телефон
int Age; // возраст
};
int main() {
setlocale(LC_ALL, "Russian");
Notes Note1= { "Грозный Иоанн Васильевич", "не установлен", 60 };
Notes Note2= { "Годунов Борис Федорович ", "095-111-2233 ", 30 };
Notes Note3= { "Романов Петр Михайлович ", "812-333-2211 ", 20 };
ofstream ofile("Notebook.dat", ios::binary);
ofile.write((char*)&Note1, sizeof(Notes)); // 1-йблок
ofile.write((char*)&Note2, sizeof(Notes)); // 2-йблок
ofile.write((char*)&Note3, sizeof(Notes)); // 3-йблок
ofile.close(); // закрытьзаписанныйфайл
ifstream ifile("Notebook.dat", ios::binary);
Notes Note; // структурированная переменная
char str[80]; // статический буфер строки
// Считывать и отображать строки в цикле, пока не eof
while (!ifile.read((char*)&Note, sizeof(Notes)).eof()) {
sprintf(str, "%s\tТел: %s\tВозраст: %d",
Note.Name, Note.Phone, Note.Age);
cout<< str << endl;
}
ifile.close(); // закрытьпрочитанныйфайл
cin.sync(); cin.get(); return 0;
}
В результате выполнения этого кода образуется бинарный файл Notebook.dat из трех блоков размером по 80 байт каждый (при условии, что символы - однобайтовые). Естественно, вы можете использовать другие поточные методы и проделывать любые операции над полями определенной структуры данных.