如何在Rust中实现类似C++ strtod的字符串转f64并获取剩余字符串?
在Rust中实现类似C++
strtod的功能 C++的strtod函数可以解析字符串前缀为double类型,同时返回剩余未解析的字符串,示例代码如下:
#include <stdio.h> #include <stdlib.h> int main(void) { char *end; double d = strtod("1.5e3randomstuff", &end); printf("%g - %s\n", d, end); }
输出结果为:1500 - randomstuff
而Rust原生的str::parse遇到无效字符会直接报错,无法返回剩余字符串,以下是几种实现等效功能的方案:
方案一:使用parse_float crate
这个第三方 crate 专门提供了类C风格的数值解析能力,可直接获取解析后的值和剩余字符串:
use parse_float::parse_float; fn main() { let s = "1.5e3randomstuff"; let (d, remainder) = parse_float::<f64>(s).unwrap(); println!("{} - {}", d, remainder); }
运行后输出:1500 - randomstuff
方案二:手动实现解析逻辑
如果不想依赖第三方库,可手动遍历字符串,定位第一个无法构成浮点数的位置,拆分后完成解析:
fn strtod(s: &str) -> Option<(f64, &str)> { // 先处理开头空白(和C的strtod行为对齐) let s = s.trim_start(); let mut iter = s.chars().enumerate(); let mut end_idx = 0; let mut has_dot = false; let mut has_exp = false; while let Some((idx, c)) = iter.next() { match c { '0'..='9' => end_idx = idx + 1, '.' if !has_dot && !has_exp => { has_dot = true; end_idx = idx + 1; } 'e' | 'E' if !has_exp => { has_exp = true; has_dot = true; // 指数后不允许出现小数点 end_idx = idx + 1; // 处理指数后的正负号 if let Some((next_idx, next_c)) = iter.next() { if next_c == '+' || next_c == '-' { end_idx = next_idx + 1; } else { // 将字符放回迭代器 iter = s.chars().enumerate().skip(next_idx); } } } '+' | '-' if idx == 0 => end_idx = idx + 1, // 处理开头的正负号 _ => break, } } if end_idx == 0 { return None; } let (num_str, remainder) = s.split_at(end_idx); num_str.parse().ok().map(|d| (d, remainder)) } fn main() { let s = "1.5e3randomstuff"; if let Some((d, remainder)) = strtod(s) { println!("{} - {}", d, remainder); } }
这段代码会输出和C++示例一致的结果,同时兼容正负号、小数点、科学计数法等常见浮点数格式。
方案三:使用nom解析库
nom是一款流行的解析器组合子库,适合构建复杂的自定义解析逻辑:
use nom::character::complete::{digit1, one_of}; use nom::combinator::{opt, recognize}; use nom::sequence::{pair, tuple}; use nom::IResult; fn parse_float(s: &str) -> IResult<&str, f64> { let sign = opt(one_of("+-")); let integer = digit1; let fractional = opt(pair(one_of("."), digit1)); let exponent = opt(pair(one_of("eE"), pair(opt(one_of("+-")), digit1))); let float_str = recognize(tuple((sign, integer, fractional, exponent))); let (remainder, s) = float_str(s)?; match s.parse() { Ok(num) => Ok((remainder, num)), Err(_) => Err(nom::Err::Failure(nom::error::Error::new(remainder, nom::error::ErrorKind::ParseTo))), } } fn main() { let s = "1.5e3randomstuff"; if let Ok((remainder, d)) = parse_float(s) { println!("{} - {}", d, remainder); } }
运行后同样能得到目标输出,适合需要扩展更复杂解析规则的场景。
内容的提问来源于stack exchange,提问作者ridiculous_fish
相关产品推荐
相关产品推荐

