rust 有没有一种方法可以轻松地使用nom请求和解析特定长度的输入?

fiei3ece  于 2023-04-21  发布在  其他
关注(0)|答案(1)|浏览(64)

给定一个数字序列b"12345678...",是否有一种简单的方法让nom将输入的前n位解析为一种类型,如u16,然后将随后的m位解析为另一种类型,依此类推。目前,nom将整个连续的数字序列消耗为一个值。
当前的应用程序是针对一个表示日期时间的数字序列,如YYYYMMDDHHmmSS,来自输入(D:20230416140523Z00'00)
我尝试组合take(n)没有成功。For instance...

use nom::bytes::complete::take;
use nom::character::complete::u16;
use nom::combinator::{flat_map};
type IResult<'a, T> = nom::IResult<&'a [u8], T, ()>;

fn parse_fixed_length_prefix<'a>(input: &[u8], n: usize) -> IResult<u16> {
    flat_map(take(n), u16::<_, ()>)(input)
}

#[test]
fn test() {
    let output = parse_fixed_length_prefix(b"123456789", 4);
    assert_eq!(output, Ok((b"56789".as_ref(), 1234u16)));
    // expected: Ok(([56789], 1234))
}

上面的方法会导致下面的编译错误,但是我怀疑有一个更好的方法可以避免这个错误。

Compiling playground v0.0.1 (/playground)
error[E0277]: expected a `FnMut<(_,)>` closure, found `Result<(_, u16), nom::Err<()>>`
   --> src/main.rs:7:5
    |
7   |     flat_map(take(n), u16::<_, ()>)(input)
    |     ^^^^^^^^ expected an `FnMut<(_,)>` closure, found `Result<(_, u16), nom::Err<()>>`
    |
    = help: the trait `FnMut<(_,)>` is not implemented for `Result<(_, u16), nom::Err<()>>`
    = help: the following other types implement trait `Parser<I, O, E>`:
              <And<F, G> as Parser<I, (O1, O2), E>>
              <AndThen<F, G, O1> as Parser<I, O2, E>>
              <Box<(dyn Parser<I, O, E> + 'a)> as Parser<I, O, E>>
              <Or<F, G> as Parser<I, O, E>>
              <nom::FlatMap<F, G, O1> as Parser<I, O2, E>>
              <nom::Into<F, O1, O2, E1, E2> as Parser<I, O2, E2>>
              <nom::Map<F, G, O1> as Parser<I, O2, E>>
    = note: required for `Result<(_, u16), nom::Err<()>>` to implement `Parser<_, _, _>`
note: required by a bound in `nom::combinator::flat_map`
   --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/nom-7.1.3/src/combinator/mod.rs:213:6
    |
213 |   H: Parser<I, O2, E>,
    |      ^^^^^^^^^^^^^^^^ required by this bound in `flat_map`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
uelo1irk

uelo1irk1#

不要使用nom:combination::flat_map。使用nom:combination::map_restake(n)的输出转换为所需的类型u16,然后使用Result类型的map将结果转换为值Ok<u16>map_res可以将其集成到其输出中,在本例中为IResult<&[u8], u16, ()>

fn parse_fixed_length_prefix<'a>(input: &[u8], n: usize) -> IResult<u16> {
    map_res(take(n), |v| u16::<_,()>(v).map(|(_,a)|a))(input)
}

我喜欢Rust的一个原因是,如果它能编译,它就能工作,甚至比Haskell还要好。

根据@ChayimFriedman的建议更新解决方案

fn parse_fixed_length_prefix<'a>(input: &[u8], n: usize) -> IResult<u16> {
    map_parser(take(n), u16)(input)
}

相关问题