You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:15:34