Запись в бинарный файл простых массивов структур

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

Поскольку из указателя на массив нельзя узнать размер массива с помощью операции sizeof, то при записи в файл в бинарном режиме нельзя взять и определить за раз, сколько байтов потребуется записать. Это немного неудобно. Тем не менее, в случае работы с массивом с зафиксированным числом элементов, когда заранее точно знаешь это число, возможно записывать в бинарный файл массив структур сразу. В таком случае будут мешать иные проблемы: выравнивания структур, использование типов внутри структур. Очень важно, чтобы в случае записи всего массива структур за раз внутри структуры не было указателей, а типы были простыми (pod-типы). Но не будем углубляться, смотрим пример:

Оборачивание структуры директивой pragma необходимо для того, чтобы типы внутри структуры не уширялись.

  • pragma pack(1) — упаковка данных, выравнивание по байту, эта прагма упаковывает структуру.
  • В коде это использовано, чтобы правильно расписать структуру, чтобы она один в один ложилась на формат бинарного файла.
Несмотря на то, что выглядит это достаточно удобным, иногда такой способ записи/чтения может тормозить работу программы. Автоматические выравнивания структур появились не сами по себе, а потому что нужно было как-то оптимизировать работу для разных платформ. Малая часть всего комплекса оптимизаций, это как раз автоматическое выравнивание структур. По той причине, что принудительные выравнивания могут тормозить работу программы, а иногда даже приводить к падению программы (как утверждают некоторые люди), некоторые авторы не рекомендуют использовать директиву pragma pack. В таком случае остаётся только поэлементная запись и, разумеется, поэлементное чтение.
Поэлементная обработка массива структур может выглядеть вот так:

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

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

Записать в файл можно и немного проще (в том смысле, что укоротить число строчек):

Но как бы то ни было, не нужно долго пояснять, что такие коды достаточно сложно воспринимать взору нашему нежному. К тому же, чтение всё равно придётся делать поэлементное. Ведь на каждом шаге цикла мы узнаём, сколько памяти надо выделить, и только узнав ОБЯЗАТЕЛЬНО выделяем память с целью записи в эту память значения. Поскольку new int[] выделяет непрерывный блок памяти, имеется возможность записи блока сразу, миную поэлементный обход, что и демонстрируется в листинге #a2.
Можно использовать более удобное в плане использования решение, описав функции записи и чтения вовнутрь самой структуры. Это будет выглядеть так:

Это можно ещё улучшать. На данный момент я некоторых вещей не знаю, поэтому пока остановлюсь на том, что уже написал. Эти примеры прямой ключ к пониманию записи простых массивов структур в файл и чтение их оттуда. Лучше всего избегать указателей, потому что даже в простой ситуации вы могли увидеть как резко осложняется код.
Иногда встречается код, в котором внутри массива структур хранятся идентичные значения. В моих примерах значениями были массивы, ёмкость которых была различной. Если бы у каждого массива ёмкость была одной и той же, то наиболее употребимый вариант для такого случая — записать сначала в файл число, обозначающее число элементов масива структур, а потом записать саму структуру, и, соответственно, прочитать сначала число, а потом элементы структуры, ориентируясь на прочитанное число. Число файл увеличит совсем на чуть-чуть, а времени сэкономить может много.

Разумеется, для того, чтобы это проделывать и хоть что-то понимать, нужны неплохие знания и структур и массивов. Поскольку в примерах задействуется указательная переменная, то, само собой, требуется некоторое понимание указателей. Случаи могут встретиться более сложные. Не всегда можно сохранить за один раз блок памяти. Чтобы понимать когда можно, когда нет, как раз и нужно понимание указателей и знание, когда выделяется непрерывный блок памяти, а когда выделенное хранится в памяти в разбросанном виде. Но новичкам, как правило, хватает варианта с записью числа в начало файла и последующей записью массива структур. При этом варианты у новичков немного проще, чем показал я. Вся эта тема только вводная в тему записи в бинарные файлы массивов структур. Как я упоминал недавно, развивать пока что не могу, потому что у меня есть определённая нехватка нужных знаний. Тем не менее, надеюсь, что эти примеры сильно помогут вам, потому что даже в 2018г. в интернете сложно найти вот именно вот это вот, когда оно многим, как мне кажется, на самом деле нужно. Могу лишь констатировать, что вам важно разобраться в ходе написания показанных кодов, чтобы задействовать это самостоятельно.
Большое спасибо участникам форума cyberforum: Dr.offset, 0x10, croessmah — за помощь с реализацией примеров. Также спасибо Evg за его ответы в одной из тем того же киберфорума.

Один комментарий на «“Запись в бинарный файл простых массивов структур”»

  1. desannn:

    пытаюсь таким образом прочесть файл таблицы  mySQL  которую предварительно пихнул в папку с exe-шником но к сожалению на экран ничего не выводит  пользуюсь msvs 2013

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/nikita-kultin/osnovy-programmirovaniya-v-turbo-c/?lfrom=15589587

Последние комментарии

Яндекс.Метрика