• Двоичный файл. Запись и чтение пользовательских типов данных в двоичные файлы

    Вероятно, вы уже встречали понятия «текстовый» и «бинарный», читая какие-нибудь статьи о файлах . И решили, что всё это для вас слишком сложно, вовек не разобраться, поэтому не стали вникать, махнув на это рукой.

    Мы же попытаемся растолковать всё максимально простым языком, ведь такие сведения полезны для каждого пользователя , даже самого неискушённого, потому что имеют непосредственное отношение к азам компьютерной безопасности .

    Немного теории

    Текстовый файл содержит в себе символы ASCII (аббревиатура расшифровывается как American Standard Code for Information Interchange, что-то вроде «американский стандарт кодировки для обмена информацией »).

    Фактически ASCII - это таблица, в которой каждой букве, цифре, знаку пунктуации и разным «собакам» со «снежинками» (в смысле, @ и *) выделено по одному байту. То бишь, по восемь нулей и единиц (бит) . Плюс, конечно, управляющие символы вроде перехода к новой строке.

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

    В бинарном файле нули и единицы выстраиваются в последовательности, которые нужны необязательно для отображения текстов (хотя есть и такие, например, *doc). А для чего же, спросите вы. Ответ прост: для всего остального. Программы, фильмы, музыка, изображения - у каждого формата свои структурные принципы организации данных.

    Само слово «бинарный» означает «состоящий из двух компонентов», «двойной». Действительно, чётко определить получается только два компонента - ноль и единицу, биты, «кирпичики», из которых сложен файл. Смысл всего остального может проявляться только во время запуска (открытия, воспроизведения).

    Изнанка цифрового мира

    Заглянуть внутрь бинарного файла можно с помощью специальной программы - HEX-редактора. (От слова Hexadecimal, обозначающего шестнадцатеричную систему счисления.) Такой софт показывает байты в виде их HEX-обозначений, расположенных фактически тоже в виде таблицы (матрицы).

    К примеру, байты изображения в формате JPEG, обычной картинки или фотографии, в окошке редактора будут показаны как FF D8 FF 00 04 3A 29 и так далее.

    Специалист поймёт, что последовательность байт FF D8 в самом начале указывает на то, что перед нами - именно JPEG. А неспециалистам всё это не так уж интересно.

    В HEX-редакторе можно открыть и текстовый файл, чтобы увидеть, какие байты соответствуют конкретным буквам (символам ASCII). Но только разве что из любопытства, всё равно смысла в этом нет.

    А вот бинарные файлы, бывает, просматривают в шестнадцатеричном виде с вполне осмысленно и конкретными целями. Например, специалисты антивирусных лабораторий таким образом ищут вредоносный код, дописанный к основному. Кстати, переходим к вопросам безопасности.

    Что способно навредить

    Текстовый файл не может содержать ничего, кроме символов ASCII. Однако программы бывают не только бинарными, но и состоящими из вышеуказанных символов. Имеются ввиду скрипты, конечно.

    Иными словами, файл *txt не заражается в принципе и угрозы не представляет. А если внутри текстового файла - скрипт, то бед натворить он способен немало.

    К примеру, файл *bat содержит код разных команд и запускается двойным кликом, как обычная программа. Написаны те команды символами ASCII, но операционная система умеет их интерпретировать - превращать в такие нули и единицы, какие характерны именно для программ.

    Но вы, конечно, не жмёте на неведомые bat-файлы, правда? Вот и хорошо.

    Предыдущие публикации:

    Последнее редактирование: 2012-11-06 14:45:16

    Метки материала: ,

    Работа с двоичными файлами

    Вся информация хранится в компьютере в виде 0 и 1, т. е. в двоичном виде. Двоичные файлы отличаются от текстовых только методами работы с ними. Например, если мы записываем в текстовый файл цифру «4», то она записывается как символ, и для ее хранения нужен один байт. Соответственно и размер файла будет равен одному байту. Текстовый файл, содержащий запись: «145687», будет иметь размер шесть байт.

    Если же записать целое число 145 687 в двоичный файл, то он будет иметь размер четыре байта, так как именно столько необходимо для хранения данных типа int. То есть двоичные файлы более компактны и в некоторых случаях более удобны для обработки.

    Запись стандартных типов данных в двоичные файлы

    Для того чтобы открыть двоичный файл, необходимо задать режим доступа ios::binary (в некоторых компиляторах C++ - ios::bin).

    Для создания выходного файла создают объект:

    ofstream outBinFile("out.bin", ios::out | ios::binary);

    /* создание объекта класса ofstream out. bin

    if (! out_f і 1) //стандартная проверка

    Запись данных происходит с помощью метода write (), который имеет два параметра: первый - указатель на начало (адрес начала) записываемых данных, второй - количество записываемых байтов. При этом указатель необходимо явно преобразовать к типу char.

    Пример 1. Записать в двоичный файл переменные различного типа:

    ofstream outBinFile("test.bin", ios::out I

    ios: :binary) ; /^создание объекта класса of stream и попытка связать его с файлом test. bin в режиме записи двоичного файла */

    int а - 145687; //объявление целой переменной а

    outBinFi le. write ((char*) &а, sizeof (а)) ; /^запись в файл

    переменной а как потока байтов, т. е. запись в файл внутреннего представления целой переменной а */ float х - 123.25; // объявление вещественной переменной х

    outBinFile .write ((char*) &х, sizeof (х)) ; /^запись в файл

    переменной х как потока байтов, т. е. запись в файл внутреннего представления целой переменной х*/

    //определение символьной переменной с и инициализация ее символом g outBinFile.write((char*)&c, sizeof(c));

    //запись символа g в файл

    outBinFile.close(); return 0;

    Если открыть содержимое файла test .bin текстовым редактором, то он будет иметь вид:

    а размер файла составит 9 байт.

    Чтение стандартных типов данных из двоичных файлов

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

    ifstream inpBinFile("inp.bin", ios::in I ios::binary);

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

    if (! inpBinFile)

    coutДля чтения данных используем функцию read(), имеющую аналогичные функции write() параметры.

    #include using namespace std; int main()

    ifstream inpBinFile("test.bin", ios::in I

    ios: : binary) ; //открываем файл на чтение в двоичном виде

    int а; float х; char с = "g";

    inpBinFile.read((char*)&a, sizeof(a));

    //читаем целочисленную переменную inpBinFile.read((char*)&x, sizeof(x));

    //читаем вещественную переменную inpBinFile.read((char*)&c, sizeof (c));

    //читаем символьную переменную

    inpBinFile.close(); cout

    Результат работы программы:

    а = 145687 х = 123.25 с = g

    Обратите внимание, что при использовании функции write и read не происходит никакого преобразования информации. В файл записывается и считывается внутреннее представление данных. Именно поэтому две предыдущие программы дали правильный результат.

    Запись и чтение пользовательских типов данных в двоичные файлы

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

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

    Пример 3. Написать программу, которая записывает сведения о группе туристов в двоичный файл.

    fstream BinFile("ankety.bin", ios::in I ios::out | ios::binary);

    Anketa Gruppa = ; for (int i = 0; i

    BinFile.write((char*)&Gruppa[i], sizeof(Anketa)); BinFile.close(); return 0;

    Пример 4. В файле «ankety.bin» содержатся данные о группе туристов, необходимо считать их и вывести на экран.

    #include using namespace std; struct Anketa {

    char name; int age;

    структурного типа данных Anketa на экран*/

    ostream& operator

    fstream BinFile("ankety.bin", ios::in | ios::out | ios::binary); if (!BinFile)

    for (int i = 0; i

    //сразу читаем все байты, занимаемые переменной типа Anketa BinFile.read((char*)&Gruppa[i], sizeof(Anketa));

    BinFile.close(); return 0;

    Результат работы программы:

    Ivanov, 23 Sidorov, 21 Petrov,22

    Для продолжения нажмите любую клавишу. . .

    Разработка собственных классов для работы с файлами

    Постоянно пользоваться методами write() и read() неудобно, гораздо приятнее иметь возможность пользоваться операциями «>» по аналогии с текстовыми файлами. Приведем пример реализации своего класса для работы с двоичными файлами.

    using namespace std;

    struct Anketa //объявляем структуру для хранения информации

    /*перегрузка операции вставки в поток для вывода пользовательского

    структурного типа данных Anketa на экран*/

    ostream& operator

    class outBinaryFile: public of stream /^определяем свой класс для работы с выходными бинарными файлами. Порождаем его от класса работы с выходными файловыми потоками*/

    /*при описании конструктора порожденного класса не забываем вызвать конструктор базового, передав ему необходимые параметры*/

    outBinaryFile(char* name) : ofstream(name, ios::out I ios::binary)

    //перегружаем необходимые операции как методы класса outBinaryFile& operator

    write((char*)&chislo, sizeof(chislo)); return *this;

    outBinaryFile& operator

    write((char*)&ank, sizeof(ank)); return *this;

    class inpBinaryFile: public if stream /* определяем свои класс для работы с входными бинарными файлами. Порождаем его от класса работы с входными файловыми потоками*/

    inpBinaryFile(char* name) : ifstream(name, ios::in I ios::binary)

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

    достаточно для конструктора порожденного класса */

    //перегружаем необходимые операции

    inpBinaryFile& operator >> (int& chislo)

    read((char*)&chislo, sizeof(chislo)); return *this;

    inpBinaryFile& operator >> (Anketa& ank)

    read((char*)&ank, sizeof(ank)); return *this;

    int а = 111, b = 112; outBinaryFile outFile("dannye.bin");

    //открываем файл на чтение

    inpBinaryFile inpFile("dannye.bin"); if (!inpFile)

    for (int і = 0; i

    inpFile >> a; //читаем анкету из файла

    cout //и выводим ее на экран

    inpFile >> anketa; cout

    Результат работы программы:

    Kolya, 1990, 582-78-95.

    Для продолжения нажмите любую клавишу. . .

    1. Можно ли в программе использовать операцию?

    ios::in I ios::out

    • а) да, в любом случае;
    • б) да, но только при работе с текстовыми файлами;
    • в) нет, в любом случае.
    • 2. Укажите правильный вариант открытия текстового файла для чтения:
      • а) ifstream inpF("input.txt", ios::in);
      • б) ifstream inpF("input.txt", ios::input);
      • в) ifstream inpF(ios:in, "input.txt").

    З.Что будет выведено на экран в результате выполнения следующего кода?

    inputFile.get(с);

    next - inputFile.peek();

    if (next == EOF)

    • а) содержимое файла, связанного с потоком inputFile, выведется на экран один раз;
    • б) содержимое файла, связанного с потоком inputFile, будет выводиться на экран бесконечное число раз;
    • в) на экран ничего не будет выведено.
    • 4. Сколько символов содержится в файле?
    • 12 3 4 5 6
    • а) 6;
    • б) 7;
    • в) 11.
    • 5. Какие методы позволяют определить конец файла?
    • а) eof();
    • б) good();
    • в) оба указанных метода.
    • 6. Для чего предназначена функция getline()?
    • а) считывает слово из файла;
    • б) считывает все содержимое файла;
    • в) считывает строку из файла.
    • 7. Чтобы записывать/считывать пользовательские типы данных в файл, необходимо:
      • а) перегрузить операции «>>» и «
      • б) запись и чтение пользовательских типов данных доступны без дополнительных действий;
      • в) запись и чтение пользовательских типов данных в файл невозможны.
    • 8. Какие функции используются для записи/чтения информации в двоичном виде?
    • а) printf / scanf;
    • б) write / read;
    • в) put / get.
    • 1. Написать программу, которая записывает в файл буквы английского алфавита.
    • 2. В файле input.txt записана информация из нескольких текстовых строк. Вывести содержимое этого файла на экран, посчитать количество строк в файле.
    • 3. На диске находится файл result.txt с результатами химических экспериментов. Написать программу, создающую копию этого файла с именем copy_resylt.txt.
    • 4. С клавиатуры ввести имя файла. В указанном файле удалить все четные строки.
    • 5. Написать программу, которая в текстовом файле, заменяет все строчные буквы прописными, и наоборот.
    • 6. В исходном текстовом файле находятся числа, разделенные пробелами. Сформировать два новых файла: первый должен содержать только четные числа, а второй - нечетные.
    • 7. В файл записаны вещественные числа. Написать программу, которая отбрасывает дробную часть у этих чисел и записывает их в новый файл.
    • 8. В текстовом файле записана информация о рейсах авиакомпании. Выбрать из этих данных рейсы, вылетающие после обеда, и вывести их на экран.
    • 9. Перегрузить операторы >> и
    • 10. Написать собственный класс для работы с бинарными файлами.
    • 11. Записать список 10 учеников класса в текстовый файл и в двоичный файл. Сравнить эти файлы. Объяснить полученное отличие.
    • 12. Разработать класс, который записывает в файл информацию об автомобилях (год выпуска, марку, цвет и т. д.) в текстовый файл. При этом каждый символ информации заменяется своим АБО 1-кодом. Полученный файл вывести на экран.

    Контрольные вопросы

    • 1. Какие классы используются для работы с файловыми потоками?
    • 2. Какие режимы доступа могут использоваться при работе с файлами? Приведите примеры.
    • 3. Какой метод служит для открытия файла? Приведите примеры.
    • 4. Какие операции доступны для работы с файлами? Какие функции предназначены для выполнения этих операций?
    • 5. Какие методы позволяют определить конец файла при чтении из него информации? В чем отличие этих методов? Приведите примеры.
    • 6. Каким образом можно считать переменные стандартных типов данных из текстовых файлов?
    • 7. Можно ли считывать из текстовых файлов переменные пользовательских типов данных?
    • 8. Какие функции предназначены для произвольного чтения информации из файла? Приведите примеры.
    • 9. Назовите особенности двоичных файлов. В чем преимущества использования таких файлов?
    • 10. С помощью каких функций можно записывать/считывать информацию в двоичные файлы?
    • 11. Как считать переменные стандартных типов данных из двоичного файла?
    • 12. Какие особенности нужно учитывать при чтении пользовательских типов данных из двоичных файлов?
    • "Ivanov", 23}, {"Sidorov", 21},

    Описание и внутреннее представление файлов

    Файлы отличаются друг от друга. Все файлы, хранящиеся в компьютере, имеют специальные атрибуты, т.е. специальные способы описания, позволяющие отличить один файл от другого: 1)имя; 2)размер; 3)дата и время; 4)значок.

    У каждого файла есть имя - имя файла. Имя файла описывает его содержимое или подсказывает, для чего он может использоваться. Имя присваивается файлу при его создании. Это относится ко всем файлам.

    Каждый файл имеет физический размер. Файл занимает некоторый объем памяти компьютера и некоторый объем дискового пространства.

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

    Каждый файл относится к определенному типу, тесно связанному со значком файла, который мы видим. Тип файла зависит от его содержимого. Каждая программа присваивает созданному документу определенный тип и соответствующий значок.

    Размер файла измеряется в байтах, как и объем памяти.

    Размер файла может составлять 0 байт, это значит, что файл существует, но ничего в себе не содержит. S Максимальный размер файла - 4 Гбайт. Но такие огромные файлы встречаются очень редко.

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

    Каждый файл имеет уникальный индекс. Индекс содержит информацию, необходимую любому процессу для того, чтобы обратиться к файлу. Процессы обращаются к файлам, используя четко определенный набор системных вызовов и идентифицируя файл строкой символов, выступающих в качестве составного имени файла. Каждое составное имя однозначно определяет файл, благодаря чему ядро системы преобразует это имя в индекс файла. Индекс включает в себя таблицу адресов расположения информации файла на диске. Так как каждый блок на диске адресуется по своему номеру, в этой таблице хранится совокупность номеров дисковых блоков. В целях повышения гибкости ядро присоединяет к файлу по одному блоку, позволяя информации файла быть разбросанной по всей файловой системе. Но такая схема размещения усложняет задачу поиска данных. Таблица адресов содержит список номеров блоков, содержащих принадлежащую файлу информацию, однако простые вычисления показывают, что линейным списком блоков файла в индексе трудно управлять. Для того, чтобы небольшая структура индекса позволяла работать с большими файлами, таблица адресов дисковых блоков приводится в соответствие со структурой.

    Текстовые и бинарные файлы

    Файлы позволяют пользователю считывать большие объемы данных непосредственно с диска, не вводя их с клавиатуры. Существуют два основных типа файлов: текстовые и двоичные.

    Текстовыми называются файлы, состоящие из любых символов. Они организуются по строкам, каждая из которых заканчивается символом «конца строки» . Конец самого файла обозначается символом «конца файла» . При записи информации в текстовый файл, просмотреть который можно с помощью любого текстового редактора, все данные преобразуются к символьному типу и хранятся в символьном виде.

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

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

    В программах на C++ при работе с текстовыми файлами необходимо подключать библиотекиifstream и iostream.

    Для того чтобы записывать данные в текстовый файл, необходимо: 1)описать переменную типа ofstream open ; 3)вывести информацию в файл; 4)обязательно закрыть файл.

    Для считывания данных из текстового файла, необходимо:

    1)описать переменную типа ifstream ; 2)открыть файл с помощью функции open ; 3)считать информацию из файла, при считывании каждой порции данных необходимо проверять, достигнут ли конец файла; 4)закрыть файл.

    Следует отметить, что во всех рассмотренных выше примерах функция fopen() в режимах “r” и “w” открывает текстовый файл на чтение и запись соответственно. Это означает, что некоторые символы форматирования текста, например возврат каретки ‘\r’ не могут быть считаны как отдельные символы, их как бы ни существует в файле, но при этом они там есть. Это особенность текстового режима файла. Для более «тонкой» работы с содержимым файлов существует бинарный режим, который представляет содержимое файла как последовательность байтов, где все возможные управляющие коды являются просто числами. Именно в этом режиме возможно удаление или добавление управляющих символов недоступных в текстовом режиме. Для того чтобы открыть файл в бинарном режиме используется также функция fopen() с последним параметром равным “rb” и “wb” соответственно для чтения и записи.

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

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

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

    #include

    #include

    #include

    using namespace std;

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

    int *mas = new int [N];

    for(i=0; 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(mas), N*sizeof(int)); //запись массива в файл

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

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

    for(i=0; i

    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(mas), N*sizeof(int)); //считывание массива из файла

    for(i=0; i

    getch(); return 0;

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

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

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

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

    fout.write(reinterpret_cast(& cb), sizeof(float));

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

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

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

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

    #include

    #include

    #include

    using namespace std;

    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(arr), dfb);

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

    for(i=0; i

    for(i=0; 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), считанные таким образом данные приобретают структуру массива и становятся полностью пригодны для каких укодно операций и преобразований.

    записей ), то вполне понятно желание как-то сократить неиспользуемый, но занимаемый объем памяти.

    Специально для таких случаев существуют записи с вариантной частью .

    Описание записи с вариантной частью

    В разделе var запись с вариантной частью описывают так:

    var <имя_записи>: record <поле1>: <тип1>; [<поле2>: <тип2>;] [...] case <поле_переключатель>: <тип> of <варианты1>: (<поле3>: <тип3>; <поле4>: <тип4>; ...); <варианты2>: (<поле5>: <тип5>; <поле6>: <тип6>; ...); [...] end;

    Невариантная часть записи (до ключевого слова case ) подчиняется тем же правилам, что и обычная запись . Вообще говоря, невариантная часть может и вовсе отсутствовать.

    Вариантная часть начинается зарезервированным словом case , после которого указывается то поле записи , которое в дальнейшем будет служить переключателем. Как и в случае обычного оператора case , переключатель обязан принадлежать к одному из перечислимых типов данных (см. лекцию 3). Список вариантов может быть константой, диапазоном или объединением нескольких констант или диапазонов. Набор полей , которые должны быть включены в структуру записи , если выполнился соответствующий вариант, заключается в круглые скобки.

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

    Графы "Название" и "Издательство" являются общими для всех трех вариантов, а остальные поля зависят от типа печатного издания. Для реализации этой структуры воспользуемся записью с вариантной частью :

    type biblio = record name,publisher: string; case item: char of "b": (author: string; year: 0..2004); "n": (data: date); "m": (year: 1700..2004; month: 1..12; number: integer); end;

    В зависимости от значения поля item , в записи будет содержаться либо 4, либо 5, либо 6 полей .

    Механизм использования записи с вариантной частью

    Количество байтов, выделяемых компилятором под запись с вариантной частью , определяется самым "длинным" ее вариантом. Более "короткие" наборы полей из других вариантов занимают лишь некоторую часть выделяемой памяти.

    В приведенном выше примере самым "длинным" является вариант " b ": для него требуется 23 байта (21 байт для строки и 2 байта для целого числа). Для вариантов " n " и " m " требуется 4 и 5 байт соответственно (см. таблицу).

    name, publisher item Вариантная часть
    ... "b" author year
    ... "n" data
    ... "m" year month number
    ... "b" author year

    Бинарные файлы

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

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