2012-06-24 5 views
4

Я пытаюсь реализовать алгоритм кодирования Хаффмана в C++.Как написать двоичный файл в C++

мой вопрос: после того, как я получил эквивалентную двоичную строку для каждого символа, как я могу записать эти нули и единицы как двоичные файлы в файле не как строку 0 или строку 1?

заранее спасибо ...

+0

Просто, чтобы сделать вещи ясно. , что я имею в виду двоичной эквивалентной строки заключается в следующем: , например, если А закодированный 010 я хочу написать 010 на файл в виде двоичного 0 и двоичном один так общая сумма составляет 3 бит НЕ 24 бит (3 байт), каждый из которых имеет двоичный эквивалент ASCII для символа 0 и символ 1. – HSN

+0

Мы не понимаем, что у вас есть, а не то, что вам нужно в конце. – akappa

+0

У меня есть структура данных (определяется пользователем), которая содержит 3 поля; символа, частоты и эквивалентного битового кодирования. сейчас, сначала я собираюсь прочитать текст из текстового файла и заполнить частотное поле для каждого символа, преобразовать структуру данных в двоичное дерево, а затем пройти дерево, чтобы найти эквивалентную битовую кодировку для каждого символа. Наконец (это мой вопрос): я хочу создать сжатую версию исходного текстового файла, прочитав каждый символ из исходного текста и напиши его эквивалентную битовую строку (используя двоичное дерево) в двоичном файле. – HSN

ответ

0

Вы не можете писать в двоичный файл только с битами; наименьший размер записанных данных - один байт (таким образом, 8 бит).

Так что вам нужно создать буфер (любой размер).

char BitBuffer; 

Запись в буфер:

int Location; 
bool Value; 

if (Value) 
    BitBuffer |= (1 << Location); 
else 
    BitBuffer &= ~(1 << Location) 

Код (1 << Location) генерирует номер со всеми 0, за исключением позиции, заданной Location. Затем, если для параметра Value установлено значение true, он устанавливает соответствующий бит в буфере 1 и 0 в другом случае. Используемые двоичные операции довольно просты, если вы их не понимаете, это должно быть в любой хорошей книге/учебнике на C++.

Местоположение должно быть в диапазоне < 0, sizeof (Buffer) -1>, поэтому < 0,7> в этом случае.

Запись буфера в файл относительно проста при использовании fstream. Просто не забудьте открыть его как двоичный.

ofstream File; 
File.open("file.txt", ios::out | ios::binary); 
File.write(BitBuffer, sizeof(char)) 

EDIT: обнаружена ошибка и исправлена.

EDIT2: Вы не можете использовать операторы << в двоичном режиме, я забыл об этом.

Альтернативное решение: Используйте std::vector<bool> или std::bitset в качестве буфера.

Это должно быть еще проще, но я думал, что смогу помочь вам немного больше.

void WriteData (std::vector<bool> const& data, std::ofstream& str) 
{ 
    char Buffer; 
    for (unsigned int i = 0; i < data.size(); ++i) 
    { 
     if (i % 8 == 0 && i != 0) 
      str.write(Buffer, 1); 
     else 
      // Paste buffer setting code here 
      // Location = i/8; 
      // Value = data[i]; 
    } 
    // It might happen that data.size() % 8 != 0. You should fill the buffer 
    // with trailing zeros and write it individually. 
} 
+0

спасибо, сэр, но у меня мало вопросов, если вы не возражаете. первая какая доза эта линия кода делает ?? int Местоположение; BitBuffer & = BitBuffer && (1 << Местоположение); , а другой вопрос: не будет ли это записать двоичную цифру эквивалентную строку ASCII на нуль или один не двоичный 0 и двоичный? – HSN

+0

упрощенный ответ: (1) буфер только одного символа бесполезен (2) std :: vector полностью сломан, и (3) std :: bitset не расходуется. – akappa

+0

@akappa i находилось под впечатлением, что 'std :: vector ' работает при индексировании с помощью собственного '[]' оператора, поэтому приведенный выше пример должен работать, даже если он далеко не оптимален. Кроме того, я прямо заявил, что буфер может быть больше, чем просто 'char', поэтому OP может в конечном итоге оказаться в массиве C-style. Сдвижные глаза кажутся мне несправедливыми. –

1

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

Это соображение предлагает использовать std::vector<bool> для выполнения вашей задачи, но это сломанное решение, потому что оно не может рассматриваться как массив c-style, и вам действительно нужно это на выходе.

Этот question спрашивает, какие из них являются действительными альтернативами std::vector<bool>, поэтому я думаю, что ответы на этот вопрос полностью соответствуют вашему вопросу.

BTW, что я хотел бы сделать, это просто обернуть std::vector<uint8_t> под класс, который подходит YOUT потребности, как код прилагается:

#include <iostream> 
#include <vector> 
#include <cstdint> 
#include <algorithm> 
class bitstream { 
private: 
    std::vector<std::uint8_t> storage; 
    unsigned int bits_used:3; 
    void alloc_space(); 
public: 
    bitstream() : bits_used(0) { } 

    void push_bit(bool bit); 

    template <typename T> 
    void push(T t); 

    std::uint8_t *get_array(); 

    size_t size() const; 

    // beware: no reference! 
    bool operator[](size_t pos) const; 
}; 

void bitstream::alloc_space() 
{ 
    if (bits_used == 0) { 
     std::uint8_t push = 0; 
     storage.push_back(push); 
    } 
} 

void bitstream::push_bit(bool bit) 
{ 
    alloc_space(); 
    storage.back() |= bit << 7 - bits_used++; 
} 

template <typename T> 
void bitstream::push(T t) 
{ 
    std::uint8_t *t_byte = reinterpret_cast<std::uint8_t*>(&t); 
    for (size_t i = 0; i < sizeof(t); i++) { 
     uint8_t byte = t_byte[i]; 
     if (bits_used > 0) { 
      storage.back() |= byte >> bits_used; 
      std::uint8_t to_push = (byte & ((1 << (8 - bits_used)) - 1)) << bits_used; 
      storage.push_back(to_push); 
     } else { 
      storage.push_back(byte); 
     } 
    } 
} 

std::uint8_t *bitstream::get_array() 
{ 
    return &storage.front(); 
} 

size_t bitstream::size() const 
{ 
    const unsigned int m = 0; 
    return std::max(m, (storage.size() - 1) * 8 + bits_used); 
} 

bool bitstream::operator[](size_t size) const 
{ 
    // No range checking 
    return static_cast<bool>((storage[size/8] >> 7 - (size % 8)) & 0x1); 
} 

int main(int argc, char **argv) 
{ 
    bitstream bs; 
    bs.push_bit(true); 
    std::cout << bs[0] << std::endl; 
    bs.push_bit(false); 
    std::cout << bs[0] << "," << bs[1] << std::endl; 
    bs.push_bit(true); 
    bs.push_bit(true); 
    std::uint8_t to_push = 0xF0; 
    bs.push_byte(to_push); 
    for (size_t i = 0; i < bs.size(); i++) 
     std::cout << bs[i] << ","; 
    std::cout << std::endl; 
} 
1

Я надеюсь, что этот код может помочь вам.

  • Вы начинаете с последовательности байтов (1s и 0s), представляющих непрерывную кодировку каждого символа входного файла.
  • Берет каждый байты последовательности и добавить немного во временные байты (char byte)
  • Каждый раз, когда вы заполняете байты, вы пишете его в файл (можно также подождать, для повышения эффективности, чтобы иметь больше данных)
  • в конце концов, вы пишете оставшиеся биты в файл, заполненный замыкающие нули, например
  • Как akappa правильно указали, else ветви может быть удалена, если byte установлен в 0 после каждой операции записи файла (или, в более общем плане, каждый раз, когда он полностью заполнен и сбрасывается где-то в другом месте), поэтому необходимо написать только 1s.

void writeBinary(char *huffmanEncoding, int sequenceLength) 
{ 
    char byte = 0; 
    // For each bit of the sequence 
    for (int i = 0; i < sequenceLength; i++) { 
     char bit = huffmanEncoding[i]; 

     // Add a single bit to byte 
     if (bit == 1) { 
      // MSB of the sequence to msb of the file 
      byte |= (1 << (7 - (i % 8))); 
      // equivalent form: byte |= (1 << (-(i + 1) % 8); 
     } 
     else { 
      // MSB of the sequence to msb of the file 
      byte &= ~(1 << (7 - (i % 8))); 
      // equivalent form: byte &= ~(1 << (-(i + 1) % 8); 
     } 

     if ((i % 8) == 0 && i > 0) { 
      //writeByteToFile(byte); 
     } 
    } 

    // Fill the last incomplete byte, if any, and write to file 
} 
+0

Итак, вы предполагаете, что каждая кодировка хранится с байтом, представляющим каждый бит? Похоже на впечатляющую трату памяти.Кстати, вы выводите биты в неправильном порядке (это должно быть '(-i)% 8'), и вы можете стереть ветвь' else', очистив 'byte' в начале после каждой записи. – akappa

+0

@akappa Это то, что OP имеет в качестве входных данных: «Я получил эквивалентную двоичную строку для каждого символа». Кроме того, я не согласен с проблемой упорядочения битов, я выводю их, поскольку они находятся в строке кодирования. Первый байт строки кодирования будет первым бит в файле. Хорошая точка для очистки другого. –

+0

первый бит в файле будет самым значительным битом вашего байта, в то время как вы первый из них будет наименее значимым из «виртуального массива байтов», который вы получите, объединив все эти биты в правильном массиве байтов. – akappa