【问题标题】:Looping over specific bytes in binary file循环二进制文件中的特定字节
【发布时间】:2017-07-26 17:16:02
【问题描述】:

我想在二进制文件中找到具有特定字节的点。例如,假设我要检查文件中以两个字节开头的所有实例:

AB C3

并以两个字节结束:

AB C4

我现在在做

    while True:
        byte = file.read(1)
         if not byte:
           break
         if ord(byte) == 171:

但是我将如何继续循环,以便一旦找到第一个 AB- 我将连续看到下一个字节是 C3。然后,一旦我找到 C3,我将如何读取字节以循环直到序列 AB C4(如果存在),而不会弄乱我的整体循环结构。

我遇到了困难,因为我不确定如何使用 python 的读取和查找功能。当我找到序列时,我应该保留一个指针来寻找吗?有没有一种简单的方法可以在我不知道的 python 中做我想做的事情?

谢谢。

【问题讨论】:

  • 文件有多大(即是否可以将其完全读入内存)?
  • 另外,你考虑过只做grep -aob "\xab\xc4" <filename>吗?
  • 你用的是什么版本的 Python?
  • 您可能可以使用正则表达式来做到这一点。见Regular expression parsing a binary file?

标签: python


【解决方案1】:

假设您可以将整个文件读入内存:

import re
import operator

with open(filename, 'rb') as file:
    bytes = file.read()

matches = [(i.start(),i.end())
            for i in re.finditer(b'\xab\xc3*\xab\xc3', bytes)]

matches 中的每个元组都包含一个开始索引和停止索引(使用切片表示法,其中停止索引是一个索引位置最后一个 c3 字节之后)。切片都是不重叠的。

如果您想要所有重叠匹配的索引,则需要按照以下方式转换 matches

overlapping = [(start, stop) 
                  for start in map(operator.itemgetter(0), matches)
                  for stop in map(operator.itemgetter(1), matches)
                      if start < stop]

【讨论】:

    【解决方案2】:

    好吧,如果您无法将整个文件读入内存,您可以通过遍历字节来完成此操作。我使用deque 作为辅助数据结构,利用maxlen 参数扫描每对连续的字节。为了让我使用 for 循环而不是容易出错的 while 循环,我使用 two-argument iter 逐字节遍历文件 a。例如iter(iterable, sentinal) 首先让我们构建一个测试用例:

    >>> import io, functools
    >>> import random
    >>> some_bytes = bytearray([random.randint(0, 255) for _ in range(12)] + [171, 195] + [88, 42, 88, 42, 88, 42] + [171, 196]+[200, 211, 141])
    >>> some_bytes
    bytearray(b'\x80\xc4\x8b\x86i\x88\xba\x8a\x8b\x07\x9en\xab\xc3X*X*X*\xab\xc4\xc8\xd3\x8d')
    >>>
    

    现在,一些预备:

    >>> from collections import deque
    >>> start = deque([b'\xab', b'\xc3'])
    >>> stop = deque([b'\xab', b'\xc4'])
    >>> current = deque(maxlen=2)
    >>> target = []
    >>> inside = False
    

    假设我们正在读取文件:

    >>> f = io.BytesIO(some_bytes)
    

    现在,创建方便的逐字节可迭代对象:

    >>> read_byte = functools.partial(f.read, 1)
    

    现在我们可以更轻松地循环:

    >>> for b in iter(read_byte, b''):
    ...     current.append(b)
    ...     if not inside and current == start:
    ...         inside = True
    ...         continue
    ...     if inside and current == stop:
    ...         break
    ...     if inside:
    ...         target.append(b)
    ...
    >>> target
    [b'X', b'*', b'X', b'*', b'X', b'*', b'\xab']
    >>>
    

    您会注意到这会将“end”的第一个值保留在其中。不过,清理起来很简单。这是一个更加充实的示例,其中分隔符之间有几个“运行”字节:

    >>> some_bytes = some_bytes * 3
    >>> start = deque([b'\xab', b'\xc3'])
    >>> stop = deque([b'\xab', b'\xc4'])
    >>> current = deque(maxlen=2)
    >>> targets = []
    >>> target = []
    >>> inside = False
    >>> f = io.BytesIO(some_bytes)
    >>> read_byte = functools.partial(f.read, 1)
    >>> for b in iter(read_byte, b''):
    ...     current.append(b)
    ...     if not inside and current == start:
    ...         inside = True
    ...         continue
    ...     if inside and current == stop:
    ...         inside = False
    ...         target.pop()
    ...         targets.append(target)
    ...         target = []
    ...     if inside:
    ...         target.append(b)
    ...
    b'\xab'
    b'\xab'
    b'\xab'
    >>> targets
    [[b'X', b'*', b'X', b'*', b'X', b'*'], [b'X', b'*', b'X', b'*', b'X', b'*'], [b'X', b'*', b'X', b'*', b'X', b'*']]
    >>>
    

    这种方法会比将文件读入内存并使用re 慢,但它会节省内存。可能有一些我没有想到的边缘情况需要处理,但我认为扩展上述方法应该很简单。此外,如果有一个“开始”字节序列而没有相应的“停止”,target 列表将继续增长,直到文件耗尽。

    最后,也许最好的方法是以可管理的块读取文件,并使用以下逻辑处理这些块。这结合了空间和时间效率。在伪伪代码中:

    chunksize = 1024
    start = deque([b'\xab', b'\xc3'])
    stop = deque([b'\xab', b'\xc4'])
    current = deque(maxlen=2)
    targets = []
    target = []
    inside = False
    read_chunk = functools.partial(f.read, chunksize)
    
    for bytes_chunk in iter(read_chunk, b''):
        for b in bytes_chunk:
            < same logic as above >
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-04
      • 2013-01-10
      • 2017-01-09
      • 2018-12-11
      • 1970-01-01
      • 2016-03-21
      • 1970-01-01
      相关资源
      最近更新 更多