【问题标题】:Split a mathematical expression while handling negative numbers with Java在使用 Java 处理负数时拆分数学表达式
【发布时间】:2013-12-30 18:25:59
【问题描述】:

我正在使用 Java 开发表达式计算器。我决定先编写一个转换为 postfix 的代码,然后编写一个反向波兰符号计算器。到目前为止,我的计算器运行良好,可以处理任何表达式,包括运算符 + - * / %

我遇到的问题是它使用空格input.split(" ") 拆分表达式,因此这意味着必须输入表达式( 4 + ( 2 * ( -2 - 1 ) ) ) * 1.5,而我应该能够输入(4+(2*(-2-1)))*1.5

经过几个小时的修修补补,我现在知道它不能工作正则表达式,但它是否能够编写一个 for 循环,一次循环遍历字符串的两个标记,如果它们都等于一个运算符,那么你可以假设第二个必须是负值。或者如果方程以运算符开头,那么它必须是负值?像这样遍历字符串,直到第二个运算符到达表达式的末尾?

这是我一直在尝试开始尝试的一些代码,但由于我对编程还是很陌生,我似乎无法让它工作。

String expression = "(4+(2*(-2--16)))*-1.5";
ArrayList<String> tokens = new ArrayList<String>();
String orig = null;
String regex = "[-+/*()]+";
String first = Character.toString(expression.charAt(0));
tokens.add(first);
for (int i = 0; i < expression.length(); i++) {
    char x = expression.charAt(i);
    String a = Character.toString(x);
    if (i >= 1){    //Check i is greater than or equal to 1
        char y = expression.charAt(i-1);
        String b = Character.toString(y);
        if(b.matches(regex) && x == '-'){
            orig = a;
        }else if(orig != null && orig.equals("-")){
            System.out.println(orig + a);
            tokens.add(orig + a);
            orig = null;
        }else{
            tokens.add(a);
        }
    }
}
for(String t:tokens){
    System.out.print(t+" ");
}

感谢您的帮助,Ciaran。

编辑:

我的问题是如何编写一种方法来拆分数学表达式,该方法在拆分时可以区分“-”作为二元运算符和“-”作为一元运算符?我对遍历字符串并比较两个标记的想法是否正确? – Ciaran Ashton 6 分钟前

我想要达到的目标 我想把String expression = (4+(2*(-2-1)))变成String[] expression = (, 4, (, 2, *, (, -2, -, 1, ), ), )

【问题讨论】:

  • 您的具体问题是什么?
  • 我如何编写一种方法来拆分数学表达式,在拆分时可以区分'-'作为二元运算符和'-'作为一元运算符?我对遍历字符串并比较两个标记的想法是否正确?
  • 顺便说一句,您的循环有错误的条件导致 outofboundexception。我
  • 当然-2是两个字符,这也是他一直在问的问题:如何正确分隔。
  • @CiaranAshton 我建议解析而不是模式匹配。解析器可以读取一个“当前字符”和一个“下一个字符”,然后根据组合进行分支 - 例如,如果当前字符是 -,则解析器可以在下一个字符是空格时分支到减法运算符并处理如果下一个字符是数字,它作为数字的一部分。它有点复杂,因为如果- 前面的字符是数字,那么我们可能希望始终将- 视为运算符,以便4-3 是减法表达式,而不是4-3 推送到堆栈上。

标签: java regex loops split calculator


【解决方案1】:

这是适合parser generator 的工作。 Java 世界中最著名的是JavaCCAntlr。我喜欢将JFlex 与JavaCC 配对使用。

它们的好处在于您可以根据上下文赋予标记不同的含义。因此,减号可能意味着一个地方的东西和另一个地方的东西不同。

【讨论】:

  • 对于看似简单的学习练习,可能有充分的理由从头开始编写一个简单的解析器,而不是跳过学习一个旨在解决更复杂问题的更复杂工具的障碍。跨度>
【解决方案2】:

使用解析器是更好的解决方案,但要按照您的要求回答您的问题,您可以使用这个正则表达式,它几乎可以满足您的需求(不是 100%,但很接近):

(?<=[\(\)\+\-*\/\^A-Za-z])|(?=[\(\)\+\-*\/\^A-Za-z])

因此,您必须将其转义并像这样使用它:

String input = ...;
String temp[] = input.split("(?<=[\\(\\)\\+\\-*\\/\\^A-Za-z])|(?=[\\(\\)\\+\\-*\\/\\^A-Za-z])");
System.out.println(Arrays.toString(temp));

输入:

7+4-(18/3)/2a^222+1ab

输出:

[7, +, 4, -, (, 18, /, 3, ), /, 2, a, ^, 222, +, 1, a, b]

在此处查看实际操作:
http://rubular.com/r/uHAObPwaln
http://ideone.com/GLFmo4

【讨论】:

  • 谢谢,但这仍然没有考虑一元运算符。
  • 我知道,这就是为什么不是 100%。你可以手动处理。
【解决方案3】:

这可以解决您的问题和像这样的问题,尽管我没有对各种数据进行彻底测试,但方法是——只要一元运算符出现在表达式中(全括号表达式),它前面都会有 '( ' 后跟一个数字。

    String expression = "(4+(2*(-2-1)))*1.5";
    List<String> tokens = new ArrayList<String>();
    String prev = null;
    int c = 0;
    for (int i = 0; i < expression.length(); i++) {
        char x = expression.charAt(i);
        String a = Character.toString(x);
        if (i >= 1 && expression.charAt(i - 1) == '(' && x == '-') {
            prev = a;
        } else {
            if (prev != null && prev.equals("-")) {
                tokens.add(prev + a);
                prev = null;
            } else {
                tokens.add(a);
            }
            c++;
        }
    }

【讨论】:

    【解决方案4】:

    这是一个只使用正则表达式的版本。它与您的示例输入匹配,但不会处理一元运算符放在括号前面或嵌套多个一元运算(例如“--1”)的情况:

    // expression that matches standard Java number formats
    // such as 1234, 12.5, and 1.3E-19
    String number = "\\d+(?:\\.\\d+(?:(?:E|e)-?\\d+)?)?";
    // expression that matches : 
    // number
    // number with unary operator (deemed unary if preceded by (,-,+,/, or *)
    // (,),-,+,/, or *
    String token = "(" + number + "|(?<=[(-+/*])-" + number + "|[-+/*()])?";
    Pattern p = Pattern.compile(token);
    Matcher m = p.matcher("(4+(2*(-2-1)))*1.5");
    while (m.find()) {
      System.out.println(m.group(0));
    }
    

    【讨论】:

      【解决方案5】:

      好吧,在这些家伙的建议之后,我创建了一个方法,该方法将接受 -1+2(4+(2*(-2--1)))*-1.5 之类的输入并将其拆分为一个数组,例如 [-1, +, 2, (, 4, +, (, 2, *, (, -2, -, -1, ), ), ), *, -1.5]

      该方法的工作方式是使用正则表达式拆分输入字符串。使用正则表达式,我能够拆分所有数字和运算符。虽然这很棒,但它无法处理负值。使用正则表达式,它总是将- 视为二元运算符。我需要它来将其视为一元运算符,以便它可以理解它是一个负值。所以我所做的是将每个运算符与它后面的字符串进行比较。如果这也是一个运算符,我知道第二个是一元运算符。然后,我还必须输入一个 if 语句来判断第一个值是否为 -,如果是,我知道那是一元运算符。

      这是目前为止的代码。我确信使用解析器有一种更简单的方法可以做到这一点,但我无法理解它。

      import java.util.ArrayList;
      import java.util.Arrays;
      
      
      public class expSplit {
      
          public String[] splitExp(String theexp){
              ArrayList<String> tokens = new ArrayList<String>();
              //System.out.println(theexp);
              String expression = theexp.replaceAll("\\s+", "");
              //System.out.println(expression);
              String tem[] = expression.split("(?<=[-+*/%(),])(?=.)|(?<=.)(?=[-+*/%(),])");
              ArrayList<String> temp = new ArrayList<String>(Arrays.asList(tem));
              String orig = null;
              String regex = "[-+/%*]+";
              String first = temp.get(0);
              tokens.add(first);
              String secound = temp.get(1);
              if(first.equals("-")){
                  tokens.remove(0);
                  tokens.add(first+secound);
              }
              for (int i = 0; i < temp.size(); i++) {
                  String a = temp.get(i);
                  if (i >= 1){
                      String b = temp.get(i-1);
                      if(b.matches(regex) && a.matches("[-+]+")){
                          String c = temp.get(i-2);
                          if(c.matches("[-+]+")){
                              //System.out.println("MATCH");
                              break;
                          }else{
                              //System.out.println("NO MATCH");
                              orig = a;
                          }
                      }else if(orig != null && orig.equals("-")){
                          tokens.add(orig + a);
                          orig = null;
                      }else{
                          tokens.add(a);
                      }
                  }
              }
              if(first.equals("+")){
                  tokens.remove(0);
              }
              if(first.equals("-")){
                  tokens.remove(1);
              }
              String[]tokenArray = new String[tokens.size()];
              tokenArray = tokens.toArray(tokenArray);
              //System.out.print(tokens);
              return tokenArray;
          }
      }
      

      谢谢你的帮助,夏兰

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-03-04
        • 2015-05-02
        • 2019-02-27
        • 1970-01-01
        • 1970-01-01
        • 2017-05-06
        • 1970-01-01
        • 2020-09-02
        相关资源
        最近更新 更多