首页 > 编程语言 >使用Java实现字符串中的表达式计算

使用Java实现字符串中的表达式计算

时间:2024-09-11 17:17:06浏览次数:8  
标签:ch Java String tokens token str 字符串 return 表达式

/**
* 计算字符串表达式的值, 不支持小数
* <ul>
* <li>加法('+')</li>
* <li>减法('-')</li>
* <li>乘法('*')</li>
* <li>除法, 保留两位小数('/')</li>
* <li>取余, 获取商('//')</li>
* <li>取余, 获取余数('%')</li>
* </ul>
*/
public class Calculator {

record Token(String str, int begin) {
}

public String eval(String expr) {
List<Token> tokens = getTokens(expr);

var value = expr(tokens);
if (!"eof".equals(tokens.getFirst().str)) {
tokens.removeLast();
String msg = "token没有被全部消耗: " + tokens.stream().map(Token::str).collect(Collectors.joining());
throw new IllegalArgumentException(msg);
}
return value.toString();
}

private List<Token> getTokens(String expr) {
List<Token> tokens = new LinkedList<>();
char[] chars = expr.toCharArray();
int i = 0;
while (i < chars.length) {
char ch = chars[i];
if (ch >= '0' && ch <= '9') {
int begin = i;
do {
i++;
} while (i < chars.length && chars[i] >= '0' && chars[i] <= '9');
String str = new String(chars, begin, i - begin);
tokens.add(new Token(str, begin));
} else if (ch == '+' || ch == '-' || ch == '*' || ch == '%') {
String str = String.valueOf(ch);
tokens.add(new Token(str, i));
i++;
} else if (ch == '/') {
if (i + 1 < chars.length && chars[i + 1] == '/') {
tokens.add(new Token("//", i));
i += 2;
} else {
tokens.add(new Token("/", i));
i++;
}
} else if (ch == '(' || ch == ')') {
String str = String.valueOf(ch);
tokens.add(new Token(str, i));
i++;
} else if (ch == ' ') {
i++;
} else {
String msg = String.format("非法字符'%c'(%d)", ch, i);
throw new IllegalArgumentException(msg);
}
}
tokens.add(new Token("eof", chars.length));
return tokens;
}

private BigDecimal expr(List<Token> tokens) {
// expr -> term | term '+' expr | term '-' expr
var value = term(tokens);

Token token = tokens.getFirst();
if ("+".equals(token.str)) {
tokens.removeFirst();
return value.add(expr(tokens));
}
if ("-".equals(token.str)) {
tokens.removeFirst();
return value.subtract(expr(tokens));
}

return value;
}

private BigDecimal term(List<Token> tokens) {
// term -> factor | factor '*' term | factor '/' term | factor '//' term | factor '%' term
var value = factor(tokens);

Token token = tokens.getFirst();
if ("*".equals(token.str)) {
tokens.removeFirst();
return value.multiply(term(tokens));
}
if ("/".equals(token.str)) {
tokens.removeFirst();
return value.divide(term(tokens), 2, RoundingMode.HALF_UP);
}
if ("//".equals(token.str)) {
tokens.removeFirst();
return value.divideAndRemainder(term(tokens))[0];
}
if ("%".equals(token.str)) {
tokens.removeFirst();
return value.divideAndRemainder(term(tokens))[1];
}

return value;
}

private BigDecimal factor(List<Token> tokens) {
// factor -> num | '-' num | '(' expr ')'
Token token = tokens.getFirst();

if ("-".equals(token.str)) {
tokens.removeFirst();
return num(tokens).negate();
}
if ("(".equals(token.str)) {
tokens.removeFirst();
var value = expr(tokens);

Token next = tokens.removeFirst();
if (!")".equals(next.str)) {
String msg = String.format("括号没有关闭 %s", token);
throw new IllegalArgumentException(msg);
}

return value;
}
return num(tokens);
}

private BigDecimal num(List<Token> tokens) {
Token token = tokens.removeFirst();
for (char ch : token.str.toCharArray()) {
if (ch < '0' || ch > '9') {
throw new IllegalArgumentException("非法数值: " + token);
}
}
return new BigDecimal(token.str);
}
}

标签:ch,Java,String,tokens,token,str,字符串,return,表达式
From: https://www.cnblogs.com/XiangHuai0v0/p/18408557

相关文章

  • JavaScript 中处理接口之字段处理(1)
     遍历 res1.data(假设它是一个数组)中的所有对象并添加两个字段的方法:letres=awaitgetData({});if(Array.isArray(res.data)){for(letitemofres1.data){item.newField1='newvalue1';item.newField2='newvalue2';}WIFIList.value=res.data......
  • Java 空值判断
    //公共方法publicstaticbooleanisNull(Objecto){booleanisNull=false;if(null==o||o.toString().isEmpty()||"null".equalsIgnoreCase(o.toString())){isNull=true;}returnisNull;}......
  • PowerDesigner 逆向工程 Could not Initialize JavaVM!
    原项目的大量的表,使用PowerDesigner进行逆向工程。提示CouldnotInitializeJavaVM! 网上找到原因,PowerDesigner不可以使用64位JDK环境! 有一种不修改环境变量的方法在PowerDesigner目录下,建立一个启动批处理,如:startup.bat,在其中配置JAVA_HOME、CLASSPATH,如下例所示: ......
  • 基于java+sql企业固定资产管理系统的计算机毕设
    摘要:固定资产管理系统是一个企事业单位不可缺少的部分,它的内容对于企事业单位的决策者和管理者来说都至关重要,所以固定资产管理系统应该能够为用户提供充足的信息和快捷的查询手段。但一直以来人们使用传统人工的方式管理固定资产的信息,这种管理方式存在着许多缺点,如:效率低、保......
  • java学习之HttpClient忽略安全证书(SSLContext)
    1.我们在写https请求时候,经常会遇见安全证书(SSL)验证失败的情况,如下图。 上图异常就是因为SSL验证失败导致的,常规的做法是忽略证书认证。方法如下:第一步:需要重写认证的证书类 X509ExtendedTrustManager。第二步:创建SSLContext对象。第三步:将SSLContext对象设置到HttpClien......
  • 中级 Java 软件工程师会遇到的事情
      计算机编程设计是一种工程学科。工程是依靠科学和时间实践才能有的经验。工程偏向的是 工程师的动手能力。科学是引导方向。C语言程序开发语言是一种软件思想知识普及的划时 代的变革。大学中学习过程序设计的学生,对于assembly  汇编,Basical  程序设计等都是十 分......
  • 第五章,java超市会员管理系统(后端)
    高级编程第五章,java超市会员管理系统(后端)一,项目概述本项目旨在开发一个超市会员管理系统,帮助超市管理会员信息、积分、优惠活动等。系统将提供用户友好的界面,方便超市员工进行日常操作。二,功能需求积分积累积分兑换查询剩余积分开卡(送100积分)退出系统三,实现分......
  • 模式-单例模式-java
    目录前言单例模式单例模式的几种实现方式饿汉式饿汉式-静态变量写法饿汉式-静态代码块写法懒汉式懒汉式-经典写法懒汉式-同步方法(不推荐)懒汉式-双重检查锁(推荐)懒汉式-静态内部类(推荐)存在的问题枚举(天然适合)总结前言最近看EffectiveJava看到了一点关于单例模式的内容,结合自己所......
  • 元组和字符串
    元组(增删查)元组在包含内容上相当于不可变的列表不可对元组内的序列修改,不可以通过索引下标的方式进行修改可进行的操作有:定义,ryhme=()通过下表索引ryhme[0]=(第一位),ryhme[-1]=(最后一位),ryhme[:3]=前3位切片,ryhme[:]=全部,ryhme[::2]=间隔2索引全部内容,ryhme[::-1]=倒序......
  • 《JavaEE进阶》----14.<SpringMVC配置文件实践之【验证码项目】>
    本篇博客介绍的是Google的开源项目Kaptcha来实现的验证码。这种是最简单的验证码。也是很常见的一种验证码。可以去看项目结果展示。就可以明白这个项目了。前言:随着安全性的要求越来越高、很多项目都使用了验证码。如今验证码的形式也是有许许多多、更复杂的图形验证码......