简体   繁体   English

使用Java处理负数时拆分数学表达式

[英]Split a mathematical expression while handling negative numbers with Java

I'm working on an expression calculator in Java. 我正在用Java开发表达式计算器。 I decided to first write a code for conversion to postfix and then write an reverse polish notation calculator. 我决定先编写一个代码以转换为后缀,然后再编写一个反向波兰表示法计算器。 So far my calculator works great and can handle any expression including operators + - * / % . 到目前为止,我的计算器运行良好,可以处理任何表达式,包括运算符+ - * / %

The problem I'm having however is that it splits the expression using a space input.split(" ") so this means the expression must be entered ( 4 + ( 2 * ( -2 - 1 ) ) ) * 1.5 when I it should be able to be entered (4+(2*(-2-1)))*1.5 . 但是,我遇到的问题是它使用空格input.split(" ")拆分了表达式,因此这意味着必须在输入表达式时输入( 4 + ( 2 * ( -2 - 1 ) ) ) * 1.5 input.split(" ") ( 4 + ( 2 * ( -2 - 1 ) ) ) * 1.5应该可以输入(4+(2*(-2-1)))*1.5

After hours of tinkering and I now know it cant work regex but would it be able to write a for loop that loops through two tokens of the string at a time and if they both equal an operator then you can assume that the second must be a negative value. 经过数小时的修改,我现在知道它不能运行正则表达式,但是它将能够编写一个for循环,一次遍历字符串的两个标记,如果它们都等于一个运算符,则可以假定第二个必须是一个运算符负值。 Or if the Equation starts with an operator then it must be a negative value? 或者,如果方程式以运算符开头,那么它必须为负值吗? Iterate through the string like this until the second operator gets to the end of the expression? 像这样遍历字符串,直到第二个运算符到达表达式的末尾?

Here is some code I have been playing with trying to make a start at this but since I'm still quite new to programming I can't seem to get it to work. 这是我一直试图尝试的一些代码,但是由于我对编程还是很陌生,所以似乎无法使其正常工作。

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+" ");
}

Thanks for any help, Ciaran. 感谢您的帮助,Ciaran。

Edit: 编辑:

My question is how can I write a method to split a mathematical expression which while splitting can tell the difference '-' as a binary operator and '-' as a unary operator? 我的问题是我该如何编写一种拆分数学表达式的方法,该方法在拆分时可以区分出作为二进制运算符的“-”和作为一元运算符的“-”? Am I on the right lines with the idea of iterating through a string and comparing the two tokens? 我是否对字符串进行迭代并比较两个标记的想法正确? – Ciaran Ashton 6 mins ago – Ciaran Ashton 6分钟前

What I am trying to achieve I want to turn String expression = (4+(2*(-2-1))) into String[] expression = (, 4, (, 2, *, (, -2, -, 1, ), ), ) 我要实现的目标是将String expression = (4+(2*(-2-1)))转换为String[] expression = (, 4, (, 2, *, (, -2, -, 1, ), ), )

This is a job for a proper parser generator . 这对于适当的解析器生成器来说是一项工作。 The best known ones in the Java world are JavaCC and Antlr . Java世界中最著名的是JavaCCAntlr I like to use JFlex paired with JavaCC. 我喜欢将JFlex与JavaCC搭配使用。

What's nice about them is that you give tokens a different meaning based on the context. 它们的好处是,您可以根据上下文为令牌赋予不同的含义。 So, a minus can mean one thing in one place and something different in another place. 因此,减号在一个地方可能意味着一件事,而在另一地方可能意味着另一件事。

Using a parser is the better solution, but to answer your question as you asked it, you can use this regex, which will pretty much do what you want (not 100% but comes close): 使用解析器是更好的解决方案,但是要按照您的要求回答您的问题,您可以使用此正则表达式,它几乎可以完成您想要的操作(不是100%,但是很接近):

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

So, you will have to escape it and use it like this: 因此,您将不得不对其进行转义并像这样使用它:

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

Input: 输入:

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

Output: 输出:

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

See it in action here: 在这里查看实际操作:
http://rubular.com/r/uHAObPwaln http://rubular.com/r/uHAObPwaln
http://ideone.com/GLFmo4 http://ideone.com/GLFmo4

This can be the solution to your problem and problem like this although i have not tested this thoroughly on variety of data but approach is that-- whenever unary operator comes in expression(fully parenthesized expression) it will be preceded by '(' and followed by a number. 这可能是解决您的问题的方法,尽管我尚未在各种数据上进行全面测试,但方法是-每当一元运算符出现在expression(全括号表达式)中时,它都将以'('开头,并在其后一个数字。

    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++;
        }
    }

This is a version that only uses regular expressions. 这是一个仅使用正则表达式的版本。 It matches your sample input but it won't handle situations where unary operators are placed in front of parentheses or if multiple unary operations are nested (eg "--1"): 它与您的样本输入匹配,但不能处理将一元运算符放在括号前面或嵌套多个一元运算符(例如“ --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));
}

Okay so after all the great advise from the guys I created a method that will take an input such as -1+2(4+(2*(-2--1)))*-1.5 and split it to an array, such as [-1, +, 2, (, 4, +, (, 2, *, (, -2, -, -1, ), ), ), *, -1.5] . 好吧,毕竟在我的指导下,我创建了一个方法,该方法将采用-1+2(4+(2*(-2--1)))*-1.5并将其拆分为一个数组,例如[-1, +, 2, (, 4, +, (, 2, *, (, -2, -, -1, ), ), ), *, -1.5]

The way the method works is that it splits the input String using regex. 该方法的工作方式是使用正则表达式拆分输入String。 With regex I was able to split all the numbers and operators. 使用正则表达式,我可以拆分所有数字和运算符。 While this is great it wasn't able to handle negative values. 尽管这很棒,但它无法处理负值。 Using regex it would always see - as a binary operator. 使用正则表达式它总是看到-二元运算符。 I needed it to see it as a unary operator so that it could understand that it's a negative value. 我需要将其视为一元运算符,以便它可以理解它是负值。 So what I did was compare each operator with the string that followed it. 所以我要做的是将每个运算符与其后的字符串进行比较。 If this was also an operator I knew that the second one was a unary operator. 如果这也是一个运算符,我知道第二个是一元运算符。 I then also had to put in an if statement for if the first value was a - and if it was I knew that that was a unary operator. 然后,我还必须输入if语句,以了解第一个值是否为-以及是否知道那是一元运算符。

Here's the code so far. 到目前为止,这是代码。 I'm sure there is an easier way to do this using a parser, I just couldn't wrap my head around it. 我敢肯定,有一种使用解析器进行此操作的简便方法,我只是无法将其包裹住。

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;
    }
}

Thanks for the help, Ciaran 感谢您的帮助,Ciaran

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM