如何使用nom解析u16输入的一部分?

时间:2019-04-14 08:49:30

标签: rust nom

给出&[u16]的原始输入流,考虑到nom期望&str作为输入,我如何使用nom对其进行解析?

例如,给定以下数据:

pub const RAW_INPUT: &[u16] = &[102, 111, 111];

我想将其解析为字符串“ foo”。

2 个答案:

答案 0 :(得分:0)

您可以通过以下两种方法进行此操作。我对modbus一无所知,因此我假设输入看起来像您上面的RAW_INPUT。首先,您可以使用as将u16强制转换为u8。这将以静默方式截断大于255的值。另一种更安全的方法是使用std::convert::TryFrom

  

简单安全的类型转换,在某些情况下可能会以受控方式失败。它是TryInto的倒数。

     

当您执行类型转换但可能成功但又需要特殊处理的类型转换时,这很有用。例如,无法使用i64特性将i32转换为From,因为i64可能包含i32不能的值代表,因此转换将丢失数据。这可以通过将i64截断为i32(本质上给i64取模i32::MAX来取值)或简单地返回i32::MAX来解决,或者其他方法From特性旨在实现完美的转换,因此TryFrom特性可以通知程序员类型转换何时会变差,并让他们决定如何处理它。

您可以在Rust Playground上试用一些说明性代码:

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;
    use std::num::TryFromIntError;
    use std::str;

    pub const RAW_BAD_INPUT: &[u16] = &[102, 111, 111, 300];
    pub const RAW_GOOD_INPUT: &[u16] = &[102, 111, 111];

    /// Converts using `as`. Demonstrates truncation.
    #[test]
    fn test_truncating() {
        let expected = vec![102, 111, 111, 44];  // Note: 44
        let actual = RAW_BAD_INPUT
            .iter()
            .map(|val| *val as u8)
            .collect::<Vec<u8>>();
        assert_eq!(expected, actual);
    }

    /// Demonstrates conversion using `TryFrom` on input with values that
    /// would be truncated
    #[test]
    fn test_try_from_bad() {
        let actual: Vec<Result<u8, TryFromIntError>> =
            RAW_BAD_INPUT.iter().map(|val| u8::try_from(*val)).collect();

        assert_eq!(actual[0].unwrap(), 102u8);
        assert_eq!(actual[1].unwrap(), 111u8);
        assert_eq!(actual[2].unwrap(), 111u8);
        assert!(actual[3].is_err());
    }

    /// Demonstrates conversion using `TryFrom` on input with values
    /// that would not be truncated. Also parses the Vec<u8> as a UTF-8
    /// encoded string
    #[test]
    fn test_try_from_ok() {
        let intermediate: Vec<u8> = RAW_GOOD_INPUT
            .iter()
            .map(|val| u8::try_from(*val).unwrap())
            .collect();
        let actual = match str::from_utf8(&intermediate) {
            Ok(s) => s,
            Err(e) => panic!("Invalid UTF-8: {}", e),
        };
        assert_eq!("foo", actual);
    }
}

使用test_try_from_ok中的代码,您现在应该有了一个String,其中包含您想用nom解析的数据。

答案 1 :(得分:0)

给出:

pub const RAW_INPUT: &[u16] = &[102, 111, 111];

我最终将输入首先转换为u8

let xs = RAW_INPUT
    .iter()
    .flat_map(|x| x.to_be_bytes().to_vec())
    .collect::<Vec<u8>>();

然后用nom正常解析。