如何在Java Stream API中实现分支式语法完成表达式分词?
用Java Stream API实现数学表达式分词
你的核心问题是原代码中连续使用.takeWhile(Character::isDigit)和.takeWhile(Character::isLetter)会导致数字流被后续的字母过滤清空,返回空字符串,进而让j始终为0引发无限循环。要实现优先匹配数字,数字匹配失败再匹配字母,最后匹配单个运算符/括号的逻辑,我们可以通过先判断当前字符类型,再针对性使用Stream来提取token的方式解决。
解决方案代码
import java.util.ArrayList; import java.util.List; public class ExprTokenizer { public static void main(String[] args) { String expr = "490 + 34 + 8 * 42 /45 -cos(90)/sin(90)"; List<String> tokens = new ArrayList<>(); int i = 0; int len = expr.length(); while (i < len) { // 跳过所有前置空格 int skipCount = (int) expr.chars().skip(i) .takeWhile(Character::isWhitespace) .count(); i += skipCount; if (i >= len) break; String token; char firstChar = expr.charAt(i); if (Character.isDigit(firstChar)) { // 提取连续数字 token = expr.chars().skip(i) .takeWhile(Character::isDigit) .mapToObj(Character::toString) .reduce("", String::concat); } else if (Character.isLetter(firstChar)) { // 提取连续字母(如函数名cos、sin) token = expr.chars().skip(i) .takeWhile(Character::isLetter) .mapToObj(Character::toString) .reduce("", String::concat); } else { // 单个运算符或括号(+、-、*、/、(、)) token = String.valueOf(firstChar); } tokens.add(token); i += token.length(); } System.out.println(tokens); } }
代码说明
- 跳过空格:先通过Stream统计并跳过当前位置的所有空格,确保后续处理的是非空白字符。
- 分支判断提取token:
- 若当前字符是数字,用
takeWhile(Character::isDigit)提取连续数字序列; - 若当前字符是字母,用
takeWhile(Character::isLetter)提取连续字母(比如函数名); - 其他情况(运算符、括号)直接取单个字符作为token。
- 若当前字符是数字,用
- 更新索引:每次提取完token后,将索引
i移动token长度,避免重复处理,也不会出现无限循环。
运行这段代码会输出你期望的结果:[490, +, 34, +, 8, *, 42, /, 45, -, cos, (, 90, ), /, sin, (, 90, )]
优化封装(可选)
如果需要复用提取逻辑,可以把字符序列提取封装成工具方法:
private static String extractContinuousChars(String expr, int startIndex, IntPredicate matchCondition) { return expr.chars().skip(startIndex) .takeWhile(matchCondition) .mapToObj(Character::toString) .reduce("", String::concat); }
然后在循环中替换对应逻辑:
if (Character.isDigit(firstChar)) { token = extractContinuousChars(expr, i, Character::isDigit); } else if (Character.isLetter(firstChar)) { token = extractContinuousChars(expr, i, Character::isLetter); } else { token = String.valueOf(firstChar); }
内容的提问来源于stack exchange,提问作者Akim
相关产品推荐
相关产品推荐

