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

如何在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);
    }
}

代码说明

  1. 跳过空格:先通过Stream统计并跳过当前位置的所有空格,确保后续处理的是非空白字符。
  2. 分支判断提取token:
    • 若当前字符是数字,用takeWhile(Character::isDigit)提取连续数字序列;
    • 若当前字符是字母,用takeWhile(Character::isLetter)提取连续字母(比如函数名);
    • 其他情况(运算符、括号)直接取单个字符作为token。
  3. 更新索引:每次提取完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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:08:08