Редактирование больших двоичных файлов

#c# #c #file

#c# #c #файл

Вопрос:

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

Но проблема в том, как я могу редактировать эти файлы. Я подумал о файловой структуре, и это будет что-то вроде этого:

[DWORD] Количество файлов

 [DWORD]Идентификатор файла
 [СТРОКА] Имя файла
 [DWORD]Размер файла
 [DWORD]FileIndex

[БАЙТЫ] Все файлы

Итак, первая часть слишком быстро получает список всех файлов, а FileIndex — это позиция в двоичном файле, поэтому я могу также установить указатель, например, 300 и прочитать файл.

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

При вставке всех файлов размер двоичного файла может составлять несколько 100 Мб.

Итак, как это делают другие программы, например, игры используют эти большие файлы, а также часто исправляют, есть ли какой-то трюк для более быстрой вставки дополнительных байтов?

Комментарии:

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

2. Да, вы правы в этом, но я просто пытаюсь улучшить свои навыки программирования и пробовать разные вещи, и прямо сейчас я работаю с файловой системой, и я мог бы разобраться в этом сам

Ответ №1:

Нет никакого «трюка» для вставки байтов в середину файла.

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

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

Использование sqlite в качестве виртуальной файловой системы может быть жизнеспособным решением этой проблемы. Но опять же, so помещает файлы данных в другую папку, чтобы они не выглядели «грязными».

Ответ №2:

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

Используя zlib (для одного примера), большая часть работы выполняется за вас (например, как показано в minizip).

Ответ №3:

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

Вы можете создать файл базы данных, который будет сопровождать вашу программу, и хранить все ваши данные там, а не в файлах. Вы даже можете встроить код базы данных в свое приложение, например, с помощью SQLite, или использовать внешние базы данных, такие как Sql Server, Oracle SQL или MySQL.

То, что вы описываете, в основном реализует вашу собственную файловую систему. Сделать это эффективным — сложная и очень сложная задача.

Ответ №4:

Вы могли бы рассматривать программу упаковки и редактирования как пользовательский распределитель памяти:

  1. Используйте минимальный размер блока — при добавлении файла используйте достаточно целых блоков, чтобы вместить файл. Это автоматически дает файлам некоторое пространство для роста, не влияя на другие.
  2. Когда файл становится слишком большим для его текущего размещения, переместите его в конец пакета.
  3. Пометьте свободные блоки как свободные и сохраните смещение к началу списка свободных в заголовке пакета. При добавлении других файлов сначала проверьте, есть ли свободный блок, достаточно большой для них.
  4. При расширении файлов за пределы их текущего блока проверьте, есть ли следующий блок в списке свободных.
  5. Если список свободных становится слишком длинным (слишком большая фрагментация), выполните консольное обновление пакета. Переместите каждый файл вперед, чтобы начать с первого свободного блока. Для этого придется переписать весь файл, но это случается редко.

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

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

Комментарии:

1. Я бы также фрагментировал сами файлы, чтобы сэкономить место.

Ответ №5:

Это не способ вставить байты в файл, отличный от того, который вы описали. Это не зависит от языка программирования. Просто так работают файловые системы…

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

Ответ №6:

Вы думали об использовании ZIP-файла? Я продолжаю видеть форматы, в которых несколько файлов хранятся как один, а базовый файл на самом деле является zip-файлом. Самое приятное в этом то, что библиотека zip обрабатывает низкоуровневое отслеживание битов за вас.

Пара примеров, которые приходят на ум:

  • Файл Word .docx на самом деле представляет собой zip (переименуйте его в .zip, и вы сможете открыть его — в нем есть целые папки)
  • Файл .xap, который используют пакеты Silverlight, является еще одним.

Комментарии:

1. .jar Файлы Java открываются с помощью zip-программ, но технически они не являются zip-файлами по какой-то технической причине, которую я не изучал.

Ответ №7:

Вы можете использовать управляемую общую память, поддерживаемую файлом с отображением памяти. У вас все равно должно быть достаточно адресного пространства для всего файла, но вам не нужно копировать весь файл в память. Вы можете использовать большинство стандартных средств с распределителем общей памяти, хотя вы можете быстро обнаружить, что указание пользовательского распределителя везде является рутиной. Но хорошая новость в том, что вам не нужно реализовывать все это самостоятельно, вы можете использовать Boost.Interprocess и у него уже есть все необходимые возможности как для unix, так и для Windows.