给定&[u16]
的原始输入流我如何使用nom来解析它,考虑到nom期望&str
作为输入?
例如,给出以下数据:
pub const RAW_INPUT: &[u16] = &[102, 111, 111];
我想将其解析为字符串“foo”。
你可以通过几种方式来做这件事。我对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
解析的数据。
鉴于:
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解析它。