使用 nom 从输入中识别浮点数
Recognize floating point from input with nom
我正在尝试使用 nom 来解析基于文本的协议。该协议可以包含以下形式的浮点值:
[-]digit+[.digit+]
例如:
- -10.0
- 10.0
- 10
我为识别这一点而构建的 nom 解析器……不太好。它也不完全是类型检查。到目前为止我得到了什么:
named!(float_prs <&[u8], f64>,
alt!(
take_while!(nom::is_digit) => {|x| FromStr::from_str(std::str::from_utf8(x).unwrap()).unwrap()} |
recognize!(chain!(
take_while!(nom::is_digit) ~
tag!(".") ~
take_while!(nom::is_digit),
|| {})
) => {|x: &[u8]| FromStr::from_str(std::str::from_utf8(x).unwrap()).unwrap() }
)
);
第一个替代解析器识别 digit+
,第二个是尝试识别 digit+.digit+
但
<nom macros>:5:38: 5:62 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
<nom macros>:5 let index = ( $ i ) . offset ( i ) ; $ crate:: IResult:: Done (
识别 -digit
等在上面没有提到。存在大量重复,意图非常模糊。有没有更好的方法来解析我没有看到的 [-]digit+[.digit+]
?
仅仅因为 nom 大量使用宏来完成它的肮脏工作,请不要忘记您仍然可以应用正常的编程最佳实践。具体来说,把问题分解成更小的部分,然后组合起来。
在 nom 中,这可以通过 chain!
宏来实现,它允许您构建组件解析器,并 named!
为它们提供有用的名称。
我建议为数字的三个部分创建子解析器 - 可选的符号、必需的整数部分和可选的小数部分。请注意,digit
已经引入了多个连续的数字字符。
此代码的主要棘手之处在于需要使用 complete!
强制 decimal
解析器为全有或全无。
#[macro_use]
extern crate nom;
use nom::digit;
named!(negative, tag!("-"));
named!(decimal, complete!(do_parse!(
tag!(".") >>
val: digit >>
(val)
)));
named!(floating_point<(Option<&[u8]>, &[u8], Option<&[u8]>)>, tuple!(
opt!(negative), digit, opt!(decimal)
));
fn main() {
println!("{:?}", floating_point(&b"0"[..]));
println!("{:?}", floating_point(&b"0."[..]));
println!("{:?}", floating_point(&b"0.0"[..]));
println!("{:?}", floating_point(&b"-0"[..]));
println!("{:?}", floating_point(&b"-0."[..]));
println!("{:?}", floating_point(&b"-0.0"[..]));
}
我没有将字节转换成有趣的东西,这会使代码混乱,而且我真的不知道如何用它做一些有用的事情。 ^_^
我最终四处摆弄并找到了我自己问题的答案。
fn float_prs(input: &[u8]) -> nom::IResult<&[u8], f64> {
let (i, name) = try_parse!(input,
recognize!(chain!(
tag!("-")? ~
take_while!(nom::is_digit) ~
tag!(".")? ~
take_while!(nom::is_digit)?,
|| {}
)));
let num: &str = std::str::from_utf8(name).unwrap();
nom::IResult::Done(i, num.parse::<f64>().unwrap())
}
Shepmaster 的解决方案也非常好,但明显缺乏 unwrap()
使用。 :)
Nom 2.1 现在有浮点解析器辅助函数;您应该不再需要手动执行此操作:
迟到的答案,但此解决方案适用于浮点数和指数。关键是要确保每个 sub-part 的数字都使用 complete!
宏。这适用于 nom 3.2
named!(
exponent,
recognize!(complete!(preceded!(
alt!(tag!("e") => { |_| b'e' } | tag!("E") => { |_| b'e'}),
pair!(opt!(tag!("-")), many1!(digit))
)))
);
named!(
decimal,
recognize!(complete!(terminated!(tag!("."), many0!(digit))))
);
named!(
floating<f32>,
map!(
recognize!(do_parse!(
opt!(tag!("-")) >> many1!(digit) >> opt!(decimal) >> opt!(exponent) >> (())
)),
|v| str::from_utf8(v).unwrap().parse::<f32>().unwrap()
)
);
#[test]
fn parse_pdl_float_test() {
assert_eq!(decimal(b".123").to_result(), Ok(".123".as_ref()));
assert_eq!(floating(b"-123").to_result(), Ok(-123.0));
assert_eq!(floating(b"123").to_result(), Ok(123.0));
assert_eq!(floating(b"-123e4").to_result(), Ok(-123e4));
assert_eq!(floating(b"123E4").to_result(), Ok(123e4));
assert_eq!(floating(b"-123e-4").to_result(), Ok(-123e-4));
assert_eq!(floating(b"-123.123e-4").to_result(), Ok(-123.123e-4));
}
我正在尝试使用 nom 来解析基于文本的协议。该协议可以包含以下形式的浮点值:
[-]digit+[.digit+]
例如:
- -10.0
- 10.0
- 10
我为识别这一点而构建的 nom 解析器……不太好。它也不完全是类型检查。到目前为止我得到了什么:
named!(float_prs <&[u8], f64>,
alt!(
take_while!(nom::is_digit) => {|x| FromStr::from_str(std::str::from_utf8(x).unwrap()).unwrap()} |
recognize!(chain!(
take_while!(nom::is_digit) ~
tag!(".") ~
take_while!(nom::is_digit),
|| {})
) => {|x: &[u8]| FromStr::from_str(std::str::from_utf8(x).unwrap()).unwrap() }
)
);
第一个替代解析器识别 digit+
,第二个是尝试识别 digit+.digit+
但
<nom macros>:5:38: 5:62 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
<nom macros>:5 let index = ( $ i ) . offset ( i ) ; $ crate:: IResult:: Done (
识别 -digit
等在上面没有提到。存在大量重复,意图非常模糊。有没有更好的方法来解析我没有看到的 [-]digit+[.digit+]
?
仅仅因为 nom 大量使用宏来完成它的肮脏工作,请不要忘记您仍然可以应用正常的编程最佳实践。具体来说,把问题分解成更小的部分,然后组合起来。
在 nom 中,这可以通过 chain!
宏来实现,它允许您构建组件解析器,并 named!
为它们提供有用的名称。
我建议为数字的三个部分创建子解析器 - 可选的符号、必需的整数部分和可选的小数部分。请注意,digit
已经引入了多个连续的数字字符。
此代码的主要棘手之处在于需要使用 complete!
强制 decimal
解析器为全有或全无。
#[macro_use]
extern crate nom;
use nom::digit;
named!(negative, tag!("-"));
named!(decimal, complete!(do_parse!(
tag!(".") >>
val: digit >>
(val)
)));
named!(floating_point<(Option<&[u8]>, &[u8], Option<&[u8]>)>, tuple!(
opt!(negative), digit, opt!(decimal)
));
fn main() {
println!("{:?}", floating_point(&b"0"[..]));
println!("{:?}", floating_point(&b"0."[..]));
println!("{:?}", floating_point(&b"0.0"[..]));
println!("{:?}", floating_point(&b"-0"[..]));
println!("{:?}", floating_point(&b"-0."[..]));
println!("{:?}", floating_point(&b"-0.0"[..]));
}
我没有将字节转换成有趣的东西,这会使代码混乱,而且我真的不知道如何用它做一些有用的事情。 ^_^
我最终四处摆弄并找到了我自己问题的答案。
fn float_prs(input: &[u8]) -> nom::IResult<&[u8], f64> {
let (i, name) = try_parse!(input,
recognize!(chain!(
tag!("-")? ~
take_while!(nom::is_digit) ~
tag!(".")? ~
take_while!(nom::is_digit)?,
|| {}
)));
let num: &str = std::str::from_utf8(name).unwrap();
nom::IResult::Done(i, num.parse::<f64>().unwrap())
}
Shepmaster 的解决方案也非常好,但明显缺乏 unwrap()
使用。 :)
Nom 2.1 现在有浮点解析器辅助函数;您应该不再需要手动执行此操作:
迟到的答案,但此解决方案适用于浮点数和指数。关键是要确保每个 sub-part 的数字都使用 complete!
宏。这适用于 nom 3.2
named!(
exponent,
recognize!(complete!(preceded!(
alt!(tag!("e") => { |_| b'e' } | tag!("E") => { |_| b'e'}),
pair!(opt!(tag!("-")), many1!(digit))
)))
);
named!(
decimal,
recognize!(complete!(terminated!(tag!("."), many0!(digit))))
);
named!(
floating<f32>,
map!(
recognize!(do_parse!(
opt!(tag!("-")) >> many1!(digit) >> opt!(decimal) >> opt!(exponent) >> (())
)),
|v| str::from_utf8(v).unwrap().parse::<f32>().unwrap()
)
);
#[test]
fn parse_pdl_float_test() {
assert_eq!(decimal(b".123").to_result(), Ok(".123".as_ref()));
assert_eq!(floating(b"-123").to_result(), Ok(-123.0));
assert_eq!(floating(b"123").to_result(), Ok(123.0));
assert_eq!(floating(b"-123e4").to_result(), Ok(-123e4));
assert_eq!(floating(b"123E4").to_result(), Ok(123e4));
assert_eq!(floating(b"-123e-4").to_result(), Ok(-123e-4));
assert_eq!(floating(b"-123.123e-4").to_result(), Ok(-123.123e-4));
}