Как сохранить вектор <bool>или битовый набор в файл, но по битам? - PullRequest
6 голосов
/ 12 января 2011

Как записать данные набора битов в файл?

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

Как бы вы это сделали?Мне действительно нужно, чтобы сохранить много истинных / ложных значений.

Ответы [ 6 ]

6 голосов
/ 12 января 2011

Самый простой подход: взять 8 последовательных логических значений, представить их как один байт, записать этот байт в ваш файл. Это сэкономит много места.

В начале файла вы можете записать количество логических значений, которые вы хотите записать в файл; это число поможет при чтении байтов из файла и преобразовании их обратно в логические значения!

2 голосов
/ 12 января 2011

Если вам нужен класс набора битов, который лучше всего поддерживает преобразование в двоичный файл, и ваш набор битов больше размера беззнакового long, то лучшим вариантом для использования будет boost :: dynamic_bitset . (Я полагаю, это больше 32 и даже 64 бит, если вы заинтересованы в экономии места).

Из dynamic_bitset вы можете использовать to_block_range для записи битов в базовый целочисленный тип. Вы можете создать dynamic_bitset обратно из блоков, используя from_block_range или его конструктор из BlockInputIterator или выполнив вызовы append ().

Теперь у вас есть байты в их собственном формате (блок), у вас все еще есть проблема записи его в поток и чтения его обратно.

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

(Примечание. Я предполагаю, что boost :: dynamic_bitset стандартно преобразует целочисленные типы одинаково, независимо от порядка следования байтов. В документации их не сказано).

Для записи двоичных чисел в поток используйте os.write( &data[0], sizeof(Block) * nBlocks ), а для чтения используйте. read( &data[0], sizeof(Block) * nBlocks ), где предполагается, что данные равны vector<Block>, и перед чтением вы должны сделать data.resize(nBlocks) (не reserve()). (Вы также можете делать странные вещи с istream_iterator или istreambuf_iterator, но resize (), вероятно, лучше).

2 голосов
/ 12 января 2011

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

template<int I>
void bitset_dump(const std::bitset<I> &in, std::ostream &out)
{
    // export a bitset consisting of I bits to an output stream.
    // Eight bits are stored to a single stream byte.
    unsigned int i = 0;  // the current bit index
    unsigned char c = 0; // the current byte
    short bits = 0;      // to process next byte
    while(i < in.size())
    {
        c = c << 1;       //
        if(in.at(i)) ++c; // adding 1 if bit is true
        ++bits;
        if(bits == 8)
        {
            out.put((char)c);
            c = 0;
            bits = 0;
        }
        ++i;
    }
    // dump remaining
    if(bits != 0) {
        // pad the byte so that first bits are in the most significant positions.
        while(bits != 8)
        {
            c = c << 1;
            ++bits;
        }
        out.put((char)c);
    }
    return;
}

template<int I>
void bitset_restore(std::istream &in, std::bitset<I> &out)
{
    // read bytes from the input stream to a bitset of size I.
    /* for debug */ //for(int n = 0; n < I; ++n) out.at(n) = false;
    unsigned int i = 0;          // current bit index
    unsigned char mask = 0x80;   // current byte mask
    unsigned char c = 0;         // current byte in stream
    while(in.good() && (i < I))
    {
        if((i%8) == 0)           // retrieve next character
        { c = in.get();
          mask = 0x80;
        }
        else mask = mask >> 1;   // shift mask
        out.at(i) = (c & mask);
        ++i;
    }
}

Обратите внимание, что, вероятно, использование reinterpret_cast части памяти, используемой набором битов в качестве массива символов, также может работать, но, возможно, оно не переносимо во всех системах, потому что вы не знаете, что такое представление набора битов (endianness ?)

1 голос
/ 12 января 2011

Одним из способов может быть:

std::vector<bool> data = /* obtain bits somehow */

// Reserve an appropriate number of byte-sized buckets.
std::vector<char> bytes((int)std::ceil((float)data.size() / CHAR_BITS)); 

for(int byteIndex = 0; byteIndex < bytes.size(); ++byteIndex) {
   for(int bitIndex = 0; bitIndex < CHAR_BITS; ++bitIndex) {
       int bit = data[byteIndex * CHAR_BITS + bitIndex];

       bytes[byteIndex] |= bit << bitIndex;
   }
}

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

(Я не доволен вычислением размера корзины, но сейчас 1 час ночи, и мне трудно думать о чем-то более элегантном).

0 голосов
/ 13 февраля 2015
#include "stdio"
#include "bitset"
...
FILE* pFile;
pFile = fopen("output.dat", "wb");
...
const unsigned int size = 1024;
bitset<size> bitbuffer;
...
fwrite (&bitbuffer, 1, size/8, pFile);
fclose(pFile);
0 голосов
/ 12 января 2011

Два варианта:

Потратьте лишние килограммы (или, скорее, пенсов) на диск побольше.

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

...