如何在Rust中对无空格代数表达式进行词元化
无空格表达式的词元解析解决方案
你的核心问题在于原代码依赖split_whitespace()分割输入,仅能处理带空格的表达式。针对无空格场景,我们可以通过正则匹配遍历的方式,逐个提取符合规则的词元,同时兼容带空格的输入。
方案一:分步匹配处理剩余字符串
这种方式逻辑清晰,便于调试,优先匹配数字(避免运算符与数字正负号混淆),再处理括号和运算符:
use regex::Regex; pub enum Type { Number(f64), Operation(char), Parentheses(char), } fn parse_expression(expression: &str) -> Option<Vec<Type>> { let mut tokens = Vec::new(); let mut remaining = expression; // 预编译正则规则 let number_re = Regex::new(r"[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?").unwrap(); let op_re = Regex::new(r"[+\-*/]").unwrap(); let paren_re = Regex::new(r"[()]").unwrap(); while !remaining.is_empty() { // 跳过空白字符,兼容带空格的输入 remaining = remaining.trim_start(); if remaining.is_empty() { break; } // 优先匹配数字(包含正负号、小数、科学计数法) if let Some(mat) = number_re.find(remaining) { let num = mat.as_str().parse().ok()?; tokens.push(Type::Number(num)); remaining = &remaining[mat.end()..]; } // 匹配括号 else if let Some(mat) = paren_re.find(remaining) { let c = mat.as_str().chars().next().unwrap(); tokens.push(Type::Parentheses(c)); remaining = &remaining[mat.end()..]; } // 匹配运算符 else if let Some(mat) = op_re.find(remaining) { let c = mat.as_str().chars().next().unwrap(); tokens.push(Type::Operation(c)); remaining = &remaining[mat.end()..]; } // 遇到无法识别的字符,返回None else { return None; } } Some(tokens) }
方案二:合并正则捕获组(更简洁)
将所有匹配规则合并到一个正则中,通过命名捕获组区分词元类型,利用captures_iter自动遍历所有匹配结果:
use regex::Regex; pub enum Type { Number(f64), Operation(char), Parentheses(char), } fn parse_expression(expression: &str) -> Option<Vec<Type>> { let mut tokens = Vec::new(); // 合并正则,数字捕获组放在最前面以优先匹配 let combined_re = Regex::new( r"(?P<number>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)|(?P<operation>[+\-*/])|(?P<parentheses>[()])" ).unwrap(); for cap in combined_re.captures_iter(expression) { if let Some(num_str) = cap.name("number") { let num = num_str.as_str().parse().ok()?; tokens.push(Type::Number(num)); } else if let Some(op_str) = cap.name("operation") { let c = op_str.as_str().chars().next().unwrap(); tokens.push(Type::Operation(c)); } else if let Some(paren_str) = cap.name("parentheses") { let c = paren_str.as_str().chars().next().unwrap(); tokens.push(Type::Parentheses(c)); } } Some(tokens) }
关键注意事项
- 正则优先级:数字正则必须放在最前面,否则像
-123这样的负号会被错误识别为减号运算符。 - 依赖说明:确保在
Cargo.toml中添加regexcrate依赖:regex = "1.10" - 错误处理:两种方案都保留了原函数的
Option返回类型,遇到无法识别的字符时返回None。 - 兼容场景:支持带空格/无空格的表达式,同时正确处理科学计数法(如
1e3)、正负号数字(如-0.5)等情况。
内容的提问来源于stack exchange,提问作者lukascobbler
相关产品推荐
相关产品推荐

