Запись битов в файл?

Я пытаюсь реализовать дерево Хаффмана.

Содержимое моего простого файла .txt, который я хочу выполнить для простого теста:

aaaaabbbbccd

Частоты символов: a:5, b:4, c:2, d:1

Кодовая таблица: (тип данных 1 и 0: строка)

a:0
d:100
c:101
b:11         

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

0000011111111101101100          

Как я могу записать побитно каждый символ этого результата в виде двоичного файла в файл ".dat"? (не как строка)


person Murat    schedule 21.12.2017    source источник
comment
Вы хотите создать файл, содержащий только эти 22 бита?   -  person Scott Hunter    schedule 21.12.2017
comment
@ Скотт Хантер Да.   -  person Murat    schedule 21.12.2017
comment
В вашей кодовой таблице есть небольшая проблема   -  person leyanpan    schedule 21.12.2017
comment
вы можете записывать только полные байты, то есть 8-битные фрагменты. Вы можете добавить байт заголовка, указывающий, сколько битов последнего байта являются допустимыми, или вы можете ввести стоп-символ, например '\0' в строках, и закодировать его, как и другие символы.   -  person Stephan Lechner    schedule 21.12.2017
comment
И вы не можете создать файл, который не содержит целых байтов, поэтому количество бит должно быть кратно 8   -  person leyanpan    schedule 21.12.2017
comment
@leyanpan: Это не очень полезный комментарий, даже без намека на то, о какой проблеме вы говорите.   -  person Scott Hunter    schedule 22.12.2017


Ответы (3)


ОП спросил:

Как я могу побитно записать каждый символ этого результата в виде двоичного файла в файл «.dat»? (не как строка)

Нельзя и вот почему...


Модель памяти

Определяет семантику хранилища памяти компьютера для абстрактной машины C++.

Память, доступная программе C++, представляет собой одну или несколько непрерывных последовательностей bytes. Каждый байт в памяти имеет уникальный адрес.

Байт

byte — это наименьшая адресуемая единица памяти. Он определяется как непрерывная последовательность битов, достаточно большая для хранения значения любой кодовой единицы UTF-8 (256 различных значений) и (since C++14) любого члена базового набора символов выполнения (96 символов которые должны быть single-byte). Подобно C, C++ поддерживает bytes размером 8 бит и выше.

Типы char, unsigned char и signed char используют один байт как для хранения, так и для представления значения. Количество битов в байте доступно как CHAR_BIT или std::numeric_limits<unsigned char>::digits.

Комплименты от cppreference.com

Вы можете найти эту страницу здесь: cppreference:memory model


Это происходит от 2017-03-21: стандарт

©ИСО/МЭК N4659

4.4 Модель памяти C++ [intro.memory]

  1. Основной единицей хранения в модели памяти C++ является байт. Байт должен быть достаточно большим, чтобы содержать любой член основного набора символов выполнения (5.3) и восьмибитных кодовых единиц формы кодирования Unicode UTF-8, и состоит из непрерывной последовательности битов,4< /sup> количество которых определяется реализацией. Самый младший бит называется младшим битом; старший бит называется старшим битом. Память, доступная программе C++, состоит из одной или нескольких последовательностей смежных байтов. Каждый байт имеет уникальный адрес.
  2. [ Примечание: Представление типов описано в 6.9. —конец примечания ]
  3. ячейка памяти — это либо объект скалярного типа, либо максимальная последовательность смежных битовых полей, имеющих ненулевую ширину. [ Примечание: различные функции языка, такие как ссылки и виртуальные функции, могут включать дополнительные области памяти, недоступные для программ, но управляемые реализацией. —конец примечания ] Два или более потоков выполнения (4.7) могут обращаться к отдельным ячейкам памяти, не мешая друг другу.
  4. [ Примечание: Таким образом, битовое поле и соседнее небитовое поле находятся в разных ячейках памяти и, следовательно, могут одновременно обновляться двумя потоками выполнения без помех. То же самое относится к двум битовым полям, если одно объявлено внутри объявления вложенной структуры, а другое нет, или если они разделены объявлением битового поля нулевой длины, или если они разделены небитовым символом. -объявление поля. Небезопасно одновременно обновлять два битовых поля в одной и той же структуре, если все поля между ними также являются битовыми полями ненулевой ширины. —конец примечания ]
  5. [ Пример: структура, объявленная как

    struct {
        char a;
        int b:5,
        c:11,
        :0,
        d:8;
        struct {int ee:8;} e;
    }
    

    содержит четыре отдельных ячейки памяти: поле a и битовые поля d и e.ee являются отдельными ячейками памяти и могут изменяться одновременно, не мешая друг другу. Битовые поля b и c вместе составляют четвертую ячейку памяти. Битовые поля b и c не могут быть изменены одновременно, но, например, b и a могут быть изменены. —конец примера ]


    4) Количество битов в байте сообщается макросом CHAR_BIT в заголовке <climits>.

Эту версию стандарта можно найти здесь: www.open-std.org раздел § 4.4 на страницах 8 и 9.


Наименьший возможный модуль памяти, который может быть записан в программе, составляет 8 или более непрерывных битов для стандартного байта. Даже с битовыми полями требование 1 byte остается в силе. Вы можете манипулировать, переключать, устанавливать отдельные биты в byte, но вы не можете записывать отдельные bits.

Что можно сделать, так это иметь буфер byte с количеством записанных битов. Когда ваши необходимые биты будут записаны, вам нужно будет пометить остальные неиспользуемые биты как padding или un-used buffer bits.

Изменить

[Примечание.] -- При использовании bit fields или unions необходимо учитывать одну вещь: endian конкретной архитектуры.

person Francis Cugler    schedule 21.12.2017

Ответ: Вы не можете.

Минимальная сумма, которую вы можете записать в файл (или прочитать из него), составляет char или unsigned char. Для всех практических целей char имеет ровно восемь бит.

Вам понадобится буфер с одним символом и подсчет количества битов, которые он содержит. Когда это число достигнет 8, вам нужно записать его и сбросить счетчик на 0. Вам также понадобится способ очистки буфера в конце. (Не то, чтобы вы не могли записать в файл 22 бита — вы можете записать только 16 или 24 бита. Вам понадобится какой-то способ пометить, какие биты в конце не используются.)

Что-то типа:

struct BitBuffer {
    FILE* file; // Initialization skipped.
    unsigned char buffer = 0;
    unsigned count = 0;

    void outputBit(unsigned char bit) {
         buffer <<= 1;         // Make room for next bit.
         if (bit) buffer |= 1; // Set if necessary.
         count++;              // Remember we have added a bit.
         if (count == 8) {
             fwrite(&buffer, sizeof(buffer), 1, file); // Error handling elided.
             buffer = 0;
             count = 0;
         }
    }
};
person Martin Bonner supports Monica    schedule 21.12.2017
comment
Функция fwrite() получает ошибку. Он должен получить 4 параметра. Я сделал некоторые изменения: fwrite(&buffer, sizeof(bit), 1, file) Верно ли это? - person Murat; 21.12.2017
comment
@Мурат: Прости за это! Я использовал sizeof(buffer), потому что вы пишете buffer. Тот факт, что bit имеет одинаковый размер, не имеет значения. В качестве альтернативы вы можете написать это как 1, потому что sizeof(unsigned char) определено как 1 (но использование константы означает, что вы не можете оптимизировать, изменив буфер на uint64_t). - person Martin Bonner supports Monica; 21.12.2017

Ответ: Можно, в некотором роде.

Здравствуйте, из своего опыта я нашел способ сделать это просто. Для задачи вам нужно определить себя и массив символов (он должен быть, например, 1 байт, он может быть больше). После этого вы должны определить функции для доступа к определенному биту из любого элемента. Например, как написать выражение для получения значения 3-го бита из char в C++.

*/*position is [1,..,n], and bytes 
are in little endian and index from 0`enter code here`*/
int bit_at(int position, unsigned char byte)
{
  return (byte & (1 << (position - 1)));
}*

Теперь вы можете видеть массив байтов как [b1,...,bn]

Теперь то, что у нас на самом деле есть в памяти, это 8 * n битов памяти. Мы можем попытаться визуализировать это так. ПРИМЕЧАНИЕ: массивы обнуляются! |0000 0000|0000 0000|...|0000 0000|

Теперь из этого вы или кто-то другой может понять, как манипулировать им, чтобы получить определенный бит из этого массива. Конечно будут какие то конверты но это не такая уж проблема. В конце концов, для кодировки, которую вы предоставляете, то есть: a: 0 d: 100 c: 101 b: 11

Мы можем закодировать сообщение abcd и создать массив, содержащий биты сообщения, используя элементы массива как массивы для битов, например:

|0111 0110|0000 0000|

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

person Goshaka_    schedule 17.12.2020