使用 nom 5.0 解析二进制文件

Binary file parsing with nom 5.0

问题

有一个文件里面有多个header,但对我来说,它只重要一个和它后面的数据。 header 在整个文件中重复多次。

它的幻数是:ASCII 中的 A3046,或 HEX 中的 0x65 0x51 0x48 0x54 0x52。 找到第一个字节后,解析器必须获取所有字节直到 0xff,然后对剩余的字节重复 headers 直到 EOF。

我的解决方案

首先我加载了文件:

let mut file = OpenOptions::new()
        .read(true)
        .open("../assets/sample")
        .unwrap();

    let mut full_file: Vec<u8> = Vec::new();
    file.read_to_end(&mut full_file);

我声明幻数:pub static QT_MAGIC: &[u8; 5] = b"A3046"; 作为测试,我编写了以下函数来尝试它是否可以找到第一个 header.

fn parse_block(input: &[u8]) -> IResult<&[u8], &[u8]> {
    tag(QT_MAGIC)(input)
}

然而,当测试运行时,Ok 具有 None 值。它肯定应该发现了什么。我做错了什么?

我没有找到使用 nom5 进行字节解析的示例,作为一个 Rust 新手也无济于事。 我如何使用这些规则解析所有块?

nom版本

首先,对此表示歉意,playground 只有 nom 4.0,因此,代码是 this github repository

要解析这样的东西,我们需要结合两个不同的解析器:

  • take_until,获取字节直到序言或 EOF
  • tag,隔离序言

还有一个组合器,preceded,所以我们可以放弃解析器序列的第一个元素。

// Our preamble
const MAGIC:&[u8] = &[0x65, 0x51, 0x48, 0x54, 0x52];
// Our EOF byte sequence
const EOF:&[u8] = &[0xff];

// Shorthand to catch EOF
fn match_to_eof(data: &[u8]) -> nom::IResult<&[u8], &[u8]> {
    nom::bytes::complete::take_until(EOF)(data)
}

// Shorthand to catch the preamble
fn take_until_preamble(data: &[u8]) -> nom::IResult<&[u8], &[u8]> {
    nom::bytes::complete::take_until(MAGIC)(data)
}
pub fn extract_from_data(data: &[u8]) -> Option<(&[u8], &[u8])> {
    let preamble_parser = nom::sequence::preceded(
        // Ditch anything before the preamble
        take_until_preamble,
        nom::sequence::preceded(
            // Ditch the preamble
            nom::bytes::complete::tag(MAGIC),
            // And take until the EOF (0xff)
            match_to_eof
        )
    );
    // And we swap the elements because it's confusing AF
    // as a return function
    preamble_parser(data).ok().map(|r| {
        (r.1, r.0)
    })
}

代码应该有足够的注释以便于遵循。这会丢弃所有字节,直到找到前导字节,然后丢弃这些字节并保留所有内容,直到找到 EOF 字节序列 ([0xff])。

然后returns一个反转nom的结果,因为它是一个例子。如果你愿意,你可以取消反转它以将它与其他解析器结合起来。第一个元素是序列的内容,第二个元素是 EOF 之后的内容。这意味着您可以使用此函数进行迭代(我在 github 上的回购中进行了测试)。