Как написать бинарный файл с длиной не кратной 8 в Python? - PullRequest
1 голос
/ 12 мая 2019

Я работаю над инструментом, создающим фиктивные двоичные файлы для проекта. У нас есть спецификация, которая описывает реальные двоичные файлы, которые создаются из потока значений с разной длиной в битах. Я использую входные и специальные файлы для создания списка значений, а класс bitstring библиотеки *1001* для преобразования значений и их объединения.

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

Есть ли другой способ, как записать биты в файл?

Ответы [ 3 ]

3 голосов
/ 12 мая 2019

Очевидно, что необработанный двоичный файл может хранить только байты, то есть кратные 8 битам.

Когда вам нужно хранить данные (биты) и метаданные (данные о данных, в данном случае длину в битах),Есть два способа:

  • выбрать или изобрести формат файла, например, структуру JSON с длиной и шестнадцатеричную разметку (неэффективно, просто пример):

    {
      "length": 11,
      "data": "c3a"
    }
    
  • использовать для метаданных файл sidecar .Это встречается реже.

1 голос
/ 12 мая 2019

Файл, как его видит большинство ОС, содержит поток байтов, иногда называемых символами. В некоторых системах существует различие между хранением текстовых и двоичных данных (например, PDP-1 использует 6-битные символы и 18-битные слова), но размер файла учитывается в этих байтах. В некоторых системах даже этот уровень не сохраняется, но используется символ конца файла, чтобы отметить, где заканчиваются данные в последнем блоке (будь то сектор, кластер или экстент).

Вам нужно будет повторить один из этих методов для хранения количества битов, например, используя заполнение 1-then-0s . Недостатком этого метода заполнения является то, что вам нужно найти конец, чтобы узнать, образует ли строка 0s (и предыдущий 1) заполнение, а не данные.

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

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

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

Одним из достаточно простых способов в цепочке битов может быть добавление (выровненного) завершающего байта к файлу, указывающего, сколько битов в предпоследнем байте заполнено:

def pad(data: bitstring.BitArray):
    padding = data.bytealign()
    data.append(bitstring.Bits(chr(padding)))
def unpad(data: bitstring.BitArray):
    padding = data[-8:].uint
    del data[-8-padding:]

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

Вот вариант 1-затем-0:

def pad(data: bitstring.BitArray):
    data.append(bitstring.Bits(length=1, uint=1))
    data.bytealign()
def unpad(data: bitstring.BitArray):
    last1 = data.rfind(bitstring.Bits(length=1, uint=1))[0]
    del data[last1:]
0 голосов
/ 12 мая 2019

Вам нужно задать дополнение, скажем, к 7-битному значению, чтобы оно соответствовало целому числу байтов:

1010101 (7 бит) -> 01010101

1111 (4 бита) -> 00001111

Заполнение старших значащих цифр не влияет на данные, взятые из файла.

...