Нахождение битового шаблона в двоичном файле с использованием Python и карты памяти - PullRequest
0 голосов
/ 15 ноября 2018

Я обрабатываю двоичный файл, который не выровнен по байту в начале. Коротко в файле есть 24-битный шаблон 0xfaf330, который является маркером синхронизации, который отмечает последующие байтовые данные. Я использую Python mmap для файла и хочу использовать Python memoryview, как только найден маркер для обработки оставшейся части файла. Итак, как мне найти 24-битный шаблон и затем использовать mmap и memoryview с этого момента?

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Ответ Мистера Мияги - хорошее решение. Другое решение использует модуль bitstring .

aFile = open(someFilePath, 'rb')
aBinaryStream = bitstring.ConstBitStream(aFile)
aTuple = aBinaryStream.find('0b111110101111001100100000') #the sync marker

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

aBuffer = aBinaryStream.read('bytes:1024') # to read 1024 bytes
0 голосов
/ 15 ноября 2018

Если вам не нужен произвольный доступ, вы можете использовать open для потоковой передачи файла.Используя file.read, вы можете получить последовательные байты из файла.Если ваш файл выровнен по байту, вы можете напрямую выполнить поиск по нему:

in_stream = open('/dev/urandom', 'rb')
# discard individual bytes until first marker byte
while in_stream.peek(1) != b'\xfa\xf3\x30':
    in_stream.read(1)
# in_stream is now positioned directly after the marker
print(in_stream.tell())

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

В качестве альтернативы, вы можете использовать результат in_stream.tell(), чтобы перейти к правильной позиции в файле mmap.


Поиск не выровненных битов

Чтобы управлять данными, не выровненными по байту, вы должны просеивать байты вручную: сдвиг битов позволяет проверять поддиапазоны байтов.Обратите внимание, что Python допускает только сдвиг битов int, но не bytes.

>>> pattern = 0xfaf330
>>> bin((pattern << 4) + 0b1011)  # pattern shifted by 4 plus garbage
0b1111101011110011001100001011

. Вы можете использовать это для сканирования окна байтов:

def find_bits(pattern: int, window: int, n: int):
    """Find an n-byte bit pattern in an n+1-byte window and return the offset"""
    for offset in range(8):
        window_slice = (window >> offset) & (2 ** (n*8) -1)
        if pattern == window_slice:
            return offset
    raise IndexError('pattern not in window')

. Вы можете снова использоватьэто для сканирования файлового потока:

in_stream = open('/dev/urandom', 'rb')
# discard individual bytes until first marker byte
while True:
    try:
        offset = find_bits(
            0xfaf330,
            int.from_bytes(in_stream.peek(3)[:4], 'big'),
            3
        )
    except IndexError:
        in_stream.read(1)
    else:
        break
# in_stream is now positioned directly after the marker
print('byte-offset:', in_stream.tell(), 'bit-offset:', offset)

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


Чтение невыровненных битов

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

def align_read(file, num_bytes: int, bit_offset: int):
    if bit_offset == 0:
        return file.read(num_bytes)
    window = file.peek(num_bytes + 1)[:num_bytes + 1]
    file.read(num_bytes)
    data = (int.from_bytes(window, 'big') >> bit_offset) & (2 ** (num_bytes*8) - 1)
    return data.to_bytes(num_bytes, 'big')
...