Если вам не нужен произвольный доступ, вы можете использовать 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')