使用 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 上的回购中进行了测试)。
问题
有一个文件里面有多个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
,获取字节直到序言或 EOFtag
,隔离序言
还有一个组合器,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 上的回购中进行了测试)。