C ++ Запись битов в двоичный файл

надеюсь, кто-то здесь может помочь. Моя проблема заключается в следующем:

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

Например:

Байт 1-4 = длина файла

Байт 5-8 = длина заголовка

Байт 8-9 = информация о версии

Байт 10-13 = время создания файла

бит 1-4 = месяц (1-12)

бит 5-9 = день (1-31)

бит 10-14 = час (0-23)

бит 15-20 = минута (0-59)

бит 21 = направление смещения UTC

бит 22-26 = час смещения UTC

бит 27-32 = минута смещения UTC

так далее…

Некоторые значения определяются статически, некоторые определяются во время выполнения. Я попытался создать «карту» заголовка, определяющую количество битов, которые должен потреблять атрибут, и значение, представленное битами. Они хранятся в векторе пар int, значением является int_pair.first, а число битов — int_pair.second. Затем я преобразовываю предоставленные значения (все целые числа) в двоичный формат и вставляю двоичную запись в поток строк. Затем я создаю набор битов из строкового представления двоичного значения и записываю его в файл. Моя проблема в том, что байты не отображаются в выходном файле в правильном порядке.

Я опущу метод для получения значений и просто предоставлю целые числа в моем примере, и для краткости урежу некоторую информацию в заголовке (поэтому в этом примере заголовок составляет 14 байтов, а не 52), но здесь примерно что я делаю

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <bitset>
#include <vector>
#include <algorithm>

int main ()
{
vector<pair<int,int>> header_vec;

header_vec.push_back(make_pair(9882719,32)); // file length
header_vec.push_back(make_pair(52,32)); // header length
header_vec.push_back(make_pair(6,3)); // high release identifier
header_vec.push_back(make_pair(4,5)); // high version identifier
header_vec.push_back(make_pair(6,3)); // low release identifier
header_vec.push_back(make_pair(4,5)); // low version identifier

// file open timestamp
header_vec.push_back(make_pair(9,4));  // month
header_vec.push_back(make_pair(6,5));  // day
header_vec.push_back(make_pair(19,5)); // hour
header_vec.push_back(make_pair(47,6)); // min
header_vec.push_back(make_pair(0,1));  // utc direction
header_vec.push_back(make_pair(0,5));  // utc offset hours
header_vec.push_back(make_pair(0,6));  // utc offset minutes

ostringstream oss;

// convert each integer to binary representation
for ( auto i : header_vec )
{
for (unsigned int j(i.second-1); j != -1; --j)
{
oss << ((i.first &(1 << j)) ? 1 : 0);
}
}

// copy oss
string str = oss.str();

// create bitset
bitset<112> header_bits(string(str.c_str()));

// write bitset to file
ofstream output("header.out", ios::out | ios::binary );
output.write( reinterpret_cast<char *>(&header_bits), 14);
output.close();

return 0;

}

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

File: header.out    (0x0e bytes)
Byte: 0x0

00    00 96 cc 5f 00 00 00 34 c4 c4 93 4e f0 00           ..._...4...N...O

0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f      0123456789abcdef

Когда на самом деле я вижу это:

File: header.out    (0x0e bytes)
Byte: 0x0

00    00 f0 4e 93 c4 c4 34 00 00 00 5f cc 96 00           @O...N...4..._..

0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f      0123456789abcdef

Я попытался изменить str до создания набора битов, но это также не дает желаемого результата.

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

Заранее спасибо…
-J

1

Решение

Написание bitset<> непосредственно как дамп памяти, безусловно, является непереносимым, о чем свидетельствует необходимость reinterpret_cast<>, Другими словами, даже если данные расположены в хорошем блоке, вы не знаете, как это сделать.

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

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

Например:

struct Timestamp
{
int month:4;
int day:5;
int hour:5;
int minute:6;
int utcOffsetDirection:1;
int utcOffsetHour:5;
int utcOffsetMinute:5;

};
1

Другие решения

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

struct timestamp{
unsigned mont:4;
unsigned day:5;
unsigned hour:5;
unsigned minute:6;
unsigned utc:1;
unsigned utc_hour:5;
unsigned utc_min:6
};struct header{
int32_t file_length;
int32_t header_lenght;
int16_t version;
timestamp tmsp;
};
1

По вопросам рекламы [email protected]