Данные, относящиеся к конкретному приложению, и как с ними обращаться?

#c# #data-structures #binary #binary-data

#c# #структуры данных #двоичный #двоичные данные

Вопрос:

Мне любопытно, как приложения генерируют свои собственные данные, которые используются с самим приложением. Например, если вы возьмете файл сохранения любой компьютерной игры или какую-нибудь программу, которая генерирует двоичные данные, такие как PSD-файлы Photoshop или .torrent-файлы для приложений BitTorrent, я бы предположил, что все они специфичны для соответствующего приложения и что авторы этого приложения запрограммировали способ создания этих данных. Мой первый вопрос: это правда? Я на 99% уверен, что это двоичные данные, потому что при открытии PSD-файла или .torrent-файла в Notepad легко увидеть, что это не то, что может быть прочитано человеком…

Мой второй вопрос: если бы я хотел создать приложение, которое генерирует свои собственные данные в двоичном формате (без обычного текста или чего-либо, чем можно легко манипулировать), как бы я поступил с этими данными? Я могу смутно представить, как сгенерировать эти данные и сохранить их в файл в двоичном формате, но я действительно зациклен на том, как я буду обрабатывать эти данные, когда они снова понадобятся приложению. Поскольку этот тип данных не является обычным текстом и не может обрабатываться как строка или что-либо подобное, как получается, что приложения создают и обрабатывают / анализируют свои собственные двоичные данные (или любые двоичные данные вообще)?

Я, очевидно, вижу, что когда вы открываете PSD-файл, Photoshop открывается и отображает все, что содержится в PSD-файле. Но как многие приложения обрабатывают эти форматы? Я просто не вижу, как проанализировать эти конкретные данные (или двоичные данные в целом) и программно делать с ними то, что вы хотите.

Ответ №1:

Что ж, в качестве простого примера давайте возьмем растровые изображения.

Растровые изображения имеют стандартную файловую структуру, которая определяется заголовком info и заголовком файла.

В статье Википедии (ссылка:http://en.wikipedia.org/wiki/BMP_file_format ) вы увидите, что заголовок info имеет четко определенный формат, так же как и заголовок файла.

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

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

Посмотрите на сериализацию, хотя это довольно широкая тема, и существует множество подходов к этому.

Редактировать: Вот пример кода (не оптимальный) для чтения (или записи, с правильными изменениями) в растровых изображениях:

 // Tell visual studio to align on 2-byte boundary
// Necessary so if you write to file, it only writes 14 bytes and not 16.
#pragma pack(2)
struct BMIH
{
    short bfType;
    long bfSize;
    short bfReserved0;
    short bfReserved1;
    long bOffbits;
};

#pragma pack(8)
struct BMFH
{
    long biSize;
    long biWidth;
    long biHeight;
    short biPlanes;
    short biBitCount;
    long biCompression;
    long biImageSize;
    long biXPelsPerMeter;
    long biYPelsPerMeter;
    long biClrUsed;
    long biClrImportant;
};



BMIH infoheader;
BMFH fileheader;

std::fstream file(filename.c_str(), std::ios::in | std::ios::binary);

// Read in info and file headers
file.read((char *) amp;infoheader, sizeof(infoheader));
file.read((char *) amp;fileheader, sizeof(fileheader));

    // Calculate size of image
int size = fileheader.biHeight * fileheader.biWidth;
int bytes = size * fileheader.biBitCount / 8;

    // Read in the image to a buffer
unsigned char data = new unsigned char[bytes];
file.read((char *) td.data, bytes);
    file.close();
  

Этот код на самом деле является радикальным упрощением и полностью игнорирует всевозможные проблемы, такие как то, что произойдет, если заголовки файла или данные повреждены, если файл не является неполным и т.д. Но это всего лишь доказательство концепции. #pragma на самом деле специфичны для Visual Studio для обеспечения правильного выравнивания заголовков.

Когда мы записываем это в файл, мы на самом деле не можем сказать «Хорошо, теперь запишите это целое число». Вместо этого мы хотим записать их в двоичном формате. Например, код, который вы могли бы (но не должны) использовать для его написания, будет выглядеть следующим образом:

 // Assume for arguments sake these data structures came pre-filled
BMFH fileheader;
BMIH infoheader;
unsigned char *data; 
int size = fileheader.biHeight * fileheader.biWidth;
int bytes = size * fileheader.biBitCount / 8;

std::fstream file("MyImage.bitmap", std::ios::out | std::ios::binary);

file.write((char *) amp;infoheader, sizeof(BMIH));
file.write((char *) amp;fileheader, sizeof(BMFH));
file.write((char *) data, sizeof(unsigned char) * bytes);
  

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

1. Спасибо за объяснение. Очень признателен. Эта большая диаграмма в Википедии была действительно полезной. Я могу начать понимать, как приложения могут обрабатывать растровое изображение … но поскольку оно в двоичном формате, как получается, что разработчики графических программ обрабатывают данные? Я так привык работать со строками, целыми числами, массивами, списками, картами и другими структурами данных, но все они содержат используемые данные, которыми можно легко манипулировать или читать как другие строки или целые числа. Я просто не вижу, как кто-то обрабатывает двоичные данные…

Ответ №2:

Прочитайте о двоичной сериализации в MSDN. Платформа .Net Framework в значительной степени помогает в этом.

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

1. Спасибо! Это чрезвычайно полезно и похоже на то, что я искал. Я собираюсь обсуждать это в течение следующих нескольких часов 🙂 Еще раз большое спасибо!

2. Согласен, MSDN обладает обширными знаниями.

Ответ №3:

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

  1. Техника двоичной сериализации
  2. Использование классов ввода-вывода для ручного чтения и записи байтов и фактическое создание файла произвольного доступа.

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

1. Большое вам спасибо. Ссылка о создании файла произвольного доступа выглядит очень интересно…